跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava算法

算法题解:旋转链表(Rotate List)

综述由AI生成给定链表向右旋转 k 个节点。核心在于处理 k 大于链表长度的情况,利用取模运算简化步数。通过计算链表长度,将尾部与头部连接成环,再找到新的断点即可实现旋转。该方法时间复杂度为 O(n),空间复杂度为 O(1)。代码提供了辅助类定义及两种实现思路,重点展示了环形链表法的优化逻辑。

念念不忘发布于 2025/1/15更新于 2026/4/264 浏览
算法题解:旋转链表(Rotate List)

题目描述

给定一个链表,将其向右旋转 k 个节点,其中 k 为非负整数。

示例

输入:1->2->3->4->5->NULL, k = 2 输出:4->5->1->2->3->NULL 解释:向右旋转 1 步:5->1->2->3->4->NULL;向右旋转 2 步:4->5->1->2->3->NULL。

辅助工具类

为了方便测试和打印链表结构,我们定义以下公共节点类。

package common;
public class ListNode {
    public int val;
    public ListNode next;
    public ListNode(int val) {
        this.val = val;
    }
    static public ListNode listNodeWithIntArray(int[] input) {
        ListNode head = new ListNode(0);
        ListNode node = head;
        for (int i : input) {
            ListNode newNode = new ListNode(i);
            node.next = newNode;
            node = node.next;
        }
        return head.next;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        ListNode node = this;
        while (node != null) {
            sb.append(node.val).append("-->");
            node = node.next;
        }
        return sb.append("Null").toString();
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        return false;
    }
}

思路一:双指针配合长度计算

由于 k 可能比链表长度大,直接旋转会导致无效操作。我们需要先获取链表长度,然后计算实际需要移动的步数。

基本逻辑是:

  1. 遍历链表得到长度 len。
  2. 有效移动步数为 k % len。
  3. 找到第 len - (k % len) 个节点作为新链表的尾部。
  4. 将原尾部连接到原头部形成环,再断开新尾部。

下面代码中使用了哑节点(dummy node)来简化边界处理,同时利用快慢指针定位断点。

public class RotateList {
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || head.next == null || k == 0) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode fast = dummy;
        ListNode slow = dummy;
        int len = 0;

        // 获取链表长度
        for (; fast.next != null; len++) {
            fast = fast.next;
        }

        // 确定需要移动的步数
        int step = len - k % len;
        for (int s = 0; s < step; s++) {
            slow = slow.next;
        }

        // 执行旋转:连成环后断开
        fast.next = dummy.next;
        dummy.next = slow.next;
        slow.next = null;
        return dummy.next;
    }

    public static void main(String[] args) {
        RotateList obj = new RotateList();
        int[] input = {1, 2, 3, 4, 5};
        ListNode initNode = ListNode.listNodeWithIntArray(input);
        System.out.println(initNode.toString());
        
        ListNode resultNode = obj.rotateRight(initNode, 2);
        System.out.println(resultNode.toString());
    }
}

这里的关键在于理解 fast 最终指向的是原链表的尾节点,而 slow 指向的是新链表的尾节点。一旦 fast.next 指向了 dummy.next(即原头节点),整个链表就变成了一个环,此时只需将 slow.next 置空即可切断环,完成旋转。

思路二:显式构建环形链表

上述方法虽然可行,但逻辑上可以进一步简化。既然要旋转,不如直接把链表首尾相接变成一个环。

  1. 遍历链表找到尾节点并计算长度。
  2. 将尾节点的 next 指向头节点。
  3. 计算新的断点位置:len - k % len。
  4. 从当前尾节点向后移动该步数,此时的节点即为新的尾节点。
  5. 更新头节点为新尾节点的下一个节点,并将新尾节点的 next 置空。

这种方法不需要额外的哑节点,内存占用更优,逻辑也更直观。

public class RotateList {
    public ListNode rotateRightWithCircle(ListNode head, int k) {
        if (head == null || head.next == null || k == 0) {
            return head;
        }
        ListNode tail = head;
        int len = 1;

        // 计算长度并找到尾节点
        while (tail.next != null) {
            tail = tail.next;
            len++;
        }

        // 形成环
        tail.next = head;
        
        // 找到新的尾节点位置
        int step = len - k % len;
        while (step > 0) {
            tail = tail.next;
            step--;
        }
        
        // 断开环,返回新头
        head = tail.next;
        tail.next = null;
        return head;
    }

    public static void main(String[] args) {
        RotateList obj = new RotateList();
        int[] input = {1, 2, 3, 4, 5};
        ListNode initNode = ListNode.listNodeWithIntArray(input);
        System.out.println(initNode.toString());
        
        ListNode resultNode = obj.rotateRightWithCircle(initNode, 2);
        System.out.println(resultNode.toString());
    }
}

运行结果

控制台输出如下,可以看到链表确实完成了预期的旋转:

1-->2-->3-->4-->5-->Null
4-->5-->1-->2-->3-->Null

总结来说,处理链表旋转问题,关键在于识别出'旋转'本质上就是'断点移动'。通过取模处理大数 k,以及利用环形结构简化指针操作,可以在 O(n) 时间和 O(1) 空间内高效解决问题。

目录

  1. 题目描述
  2. 示例
  3. 辅助工具类
  4. 思路一:双指针配合长度计算
  5. 思路二:显式构建环形链表
  6. 运行结果
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 从 try-catch 到链式调用:一种更优雅的 async/await 错误处理方案
  • 申请 Hugging Face 访问令牌(以 Meta-Llama-3.1-8B-Instruct 为例)
  • Python 多线程日志错乱:logging.Handler 的并发问题
  • 2025 电商客服机器人实测:乐言、店小蜜等五家主流品牌对比
  • YOLOv8 旋转框角度回归优化:CSL 与 DCL 编码实战
  • EhViewer:安卓开源漫画阅读器安装与使用指南
  • VS Code 集成 Overleaf 实现本地 AI 辅助 LaTeX 写作
  • STL map/multimap 深度解析:接口用法与核心特性
  • UI UX Pro Max:AI 驱动的现代前端 UI 工作流实战
  • GitHub Copilot Pro 学生免费权益获取与 VS Code 配置指南
  • AIGC 内容创作实战指南:基于 TRAE 框架与大语言模型提效方案
  • Spring Boot 与 Vue 3 实时游戏匹配实战:WebSocket 前后端对接
  • ComfyUI 基于 CNB 平台的云端搭建与使用指南
  • Ubuntu 20.04 安装 Ollama 及 Open WebUI 部署大型语言模型
  • 前端多版本零 404 部署实践与解决方案
  • OpenClaw 漏洞预警:AI 代理安全与日志审计方案
  • Windows 系统部署龙虾 AI(OpenClaw)指南
  • Android 开发环境兼容性指南:API、JDK、AGP 与 Gradle 版本匹配
  • 大模型融合算法原理与 mergekit 工具实践
  • 国内环境部署 n8n 与私有 AI 模型实战指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online