LeetCode 61. 旋转链表:题解+思路拆解

LeetCode中等难度题目「旋转链表」,这道题核心考察链表的遍历、长度计算和闭环处理,看似简单但容易踩坑,尤其是k值大于链表长度的情况,咱们一步步拆解思路、分析代码,帮大家吃透这道题。

一、题目描述(简化版)

给你一个单链表的头节点head,将链表每个节点向右移动k个位置,返回旋转后的链表头节点。

举个例子更直观:

  • 输入:head = [1,2,3,4,5], k = 2 → 输出:[4,5,1,2,3]

  • 输入:head = [0,1,2], k = 4 → 输出:[2,0,1](因为k=4,链表长度3,4%3=1,实际只需移动1位)

二、核心思路拆解

旋转链表的本质,是「将链表尾部的k个节点,移到链表头部」,但直接移动尾部节点会很繁琐(单链表无法直接获取前驱节点),所以我们换个更高效的思路,分4步走:

1. 边界处理(避免无效操作)

如果链表为空(head=null),或者k=0(不需要移动),直接返回原链表即可,这一步能减少后续无效计算。

2. 计算链表长度 + 闭环链表

遍历链表,一方面统计链表的总长度len,另一方面找到链表的尾节点p(遍历结束时p指向最后一个节点)。

此时将尾节点p的next指向头节点head,形成一个「闭环链表」------ 这样后续移动节点时,无需单独处理尾部和头部的衔接,简化操作。

3. 简化k值(关键一步)

当k大于链表长度len时,比如k=4、len=3,移动4次和移动1次的结果完全一致(因为移动len次会回到原链表)。

所以用k = k % len 简化k值,若简化后k=0,说明移动len的整数倍,直接返回原链表。

4. 找到新的头节点和尾节点,断开闭环

旋转后,新的尾节点是「原链表的第 len - k - 1 个节点」,新的头节点是新尾节点的next。

举个例子:head=[1,2,3,4,5],len=5,k=2 → len - k -1 = 2 → 新尾节点是第3个节点(值为3),新头节点是4;之后将新尾节点的next设为null,断开闭环,就得到旋转后的链表。

三、完整代码(TypeScript)

先定义链表节点类(题干已给出,直接复用),再实现旋转函数,每一步都加了注释,对应上面的思路:

typescript 复制代码
// 链表节点类(题干自带)
class ListNode {
  val: number
  next: ListNode | null
  constructor(val?: number, next?: ListNode | null) {
    this.val = (val === undefined ? 0 : val)
    this.next = (next === undefined ? null : next)
  }
}

// 旋转链表核心函数
function rotateRight(head: ListNode | null, k: number): ListNode | null {
  // 边界处理:空链表或无需移动
  if (head === null || k === 0) {
    return head
  }

  let len = 1 // 链表长度,初始为1(至少有头节点)
  let p: ListNode | null = head // 用于遍历链表,找尾节点

  // 1. 遍历链表,计算长度 + 找到尾节点
  while (p?.next !== null) {
    len++
    p = p.next;
  }

  // 2. 简化k值,避免无效循环
  k = k % len;
  if (k === 0) { // 移动len的整数倍,链表不变
    return head;
  }

  // 3. 找到新尾节点(原链表第 len - k - 1 个节点)
  let newTail: ListNode | null = head;
  for (let i = 0; i < len - k - 1; i++) {
    newTail = newTail!.next; // 非空断言,因为前面已确认链表非空且k>0
  }

  // 4. 确定新头节点,断开闭环
  const res = newTail!.next; // 新头节点是新尾节点的next
  newTail!.next = null; // 断开闭环
  p!.next = head; // 尾节点指向原头节点,衔接头部

  return res; // 返回新头节点
};

四、易错点提醒(避坑关键)

  • 易错点1:忽略k大于链表长度的情况 → 必须用k = k % len简化,否则会陷入无效遍历。

  • 易错点2:忘记闭环链表 → 直接移动尾部节点会无法衔接头部,闭环后只需断开一次即可。

  • 易错点3:新尾节点的位置计算错误 → 记住公式:新尾节点索引 = len - k - 1(索引从0开始)。

  • 易错点4:空指针异常 → 遍历尾节点时,注意p?.next的判断(可选链操作),避免p为null时调用next。

五、复杂度分析

  • 时间复杂度:O(n),其中n是链表长度。只需遍历链表2次(一次计算长度,一次找新尾节点),无嵌套循环。

  • 空间复杂度:O(1),只使用了几个指针变量(p、newTail等),未使用额外空间,符合原地算法要求。

六、拓展思考

如果这道题要求「向左移动k个位置」,思路该如何调整?

提示:向左移动k个位置,等价于向右移动 len - k % len 个位置,只需修改k的简化逻辑即可,大家可以动手试试。

最后,这道题是单链表操作的经典题目,核心是理解「闭环简化衔接」和「k值简化」的思路,多敲几遍代码,就能熟练掌握啦。如果有疑问,欢迎在评论区交流。

相关推荐
Felven2 小时前
D. Find the Different Ones!
算法
mit6.8242 小时前
logtrick
算法
风象南2 小时前
用 ASCII 草图 + AI 快速生成前端代码
前端
_OP_CHEN2 小时前
【前端开发之JavaScript】(三)JS基础语法中篇:运算符 / 条件 / 循环 / 数组一网打尽
开发语言·前端·javascript·网页开发·图形化界面·语法基础·gui开发
zheshiyangyang2 小时前
前端面试基础知识整理【Day-5】
前端·面试·职场和发展
mit6.82410 小时前
Xai架构
算法
WBluuue10 小时前
Codeforces 1078 Div2(ABCDEF1)
c++·算法
寻星探路11 小时前
【JVM 终极通关指南】万字长文从底层到实战全维度深度拆解 Java 虚拟机
java·开发语言·jvm·人工智能·python·算法·ai
岱宗夫up11 小时前
FastAPI入门(上篇):快速构建高性能Python Web API
开发语言·前端·python·fastapi