LeetCode 第61题:旋转链表
题目描述
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
难度
中等
题目链接
示例
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
提示
- 链表中节点的数目在范围
[0, 500]内 -100 <= Node.val <= 1000 <= k <= 2 * 10^9
解题思路
闭合成环后断开
这道题可以通过以下步骤解决:
- 首先计算链表长度,并找到尾节点
- 将尾节点与头节点相连,形成环
- 找到新的断开点,即倒数第k个节点的前一个节点
- 断开环,返回新的头节点
关键点:
- 处理k大于链表长度的情况
- 注意k为0或链表为空的边界情况
- 计算实际需要移动的步数
- 正确找到断开点的位置
具体步骤:
- 遍历链表得到长度n和尾节点tail
- 计算实际需要移动的步数k = k % n
- 如果k为0,直接返回原链表
- 将尾节点与头节点相连
- 移动到新的断开点
- 断开环并返回新的头节点
图解思路
算法步骤分析表
| 步骤 | 操作 | 链表状态 | 说明 |
|---|---|---|---|
| 初始 | - | 1->2->3->4->5 | 原始链表 |
| 计算长度 | 遍历 | 1->2->3->4->5 | 长度为5 |
| 形成环 | 连接 | 1->2->3->4->5->1 | 首尾相连 |
| 找断点 | 移动 | 1->2->3->4->5->1 | 在3处断开 |
| 最终 | 断开 | 4->5->1->2->3 | 完成旋转 |
状态/情况分析表
| 情况 | 输入 | 输出 | 说明 |
|---|---|---|---|
| 空链表 | [] | [] | 特殊情况 |
| k=0 | [1,2,3] | [1,2,3] | 无需旋转 |
| k=len | [1,2,3] | [1,2,3] | 旋转一圈 |
代码实现
C# 实现
public class Solution {
public ListNode RotateRight(ListNode head, int k) {
// 处理特殊情况
if (head == null || head.next == null || k == 0) return head;
// 计算链表长度并找到尾节点
ListNode tail = head;
int length = 1;
while (tail.next != null) {
tail = tail.next;
length++;
}
// 计算实际需要移动的步数
k = k % length;
if (k == 0) return head;
// 将链表首尾相连
tail.next = head;
// 找到新的断开点
ListNode newTail = head;
for (int i = 0; i < length - k - 1; i++) {
newTail = newTail.next;
}
// 断开环
ListNode newHead = newTail.next;
newTail.next = null;
return newHead;
}
}
Python 实现
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
# 处理特殊情况
if not head or not head.next or k == 0:
return head
# 计算链表长度并找到尾节点
tail = head
length = 1
while tail.next:
tail = tail.next
length += 1
# 计算实际需要移动的步数
k = k % length
if k == 0:
return head
# 将链表首尾相连
tail.next = head
# 找到新的断开点
new_tail = head
for i in range(length - k - 1):
new_tail = new_tail.next
# 断开环
new_head = new_tail.next
new_tail.next = None
return new_head
C++ 实现
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
// 处理特殊情况
if (!head || !head->next || k == 0) return head;
// 计算链表长度并找到尾节点
ListNode* tail = head;
int length = 1;
while (tail->next) {
tail = tail->next;
length++;
}
// 计算实际需要移动的步数
k = k % length;
if (k == 0) return head;
// 将链表首尾相连
tail->next = head;
// 找到新的断开点
ListNode* newTail = head;
for (int i = 0; i < length - k - 1; i++) {
newTail = newTail->next;
}
// 断开环
ListNode* newHead = newTail->next;
newTail->next = nullptr;
return newHead;
}
};
执行结果
- 执行用时:84 ms
- 内存消耗:37.8 MB
代码亮点
- 🎯 巧妙利用取模运算处理大k值
- 💡 通过闭合成环简化问题
- 🔍 优化了特殊情况的处理
- 🎨 代码结构清晰,易于理解
常见错误分析
- 🚫 没有处理空链表或单节点链表
- 🚫 k值取模计算错误
- 🚫 断开位置计算错误
- 🚫 忘记处理k为0的情况
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 数组存储 | O(n) | O(n) | 直观简单 | 空间消耗大 |
| 两次遍历 | O(n) | O(1) | 空间优化 | 需遍历两次 |
| 闭合成环 | O(n) | O(1) | 最优解法 | 需要注意边界 |