链表题的典型思路就是找到关键的结点,然后改变相关结点的指向即可。
总结:
-
如果要改变头结点的,如需要删除头结点时,则为了使操作所有结点的方式统一,我们会选择建立虚拟头结点
dummy。 -
快慢指针法:
① 快指针一次走两步,慢指针一次走一步。
② 快指针先走
k步,然后快慢指针一起走。③ 上述两个策略相结合。
-
头插法,尾插法等。
61. 旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 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
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
思路:
- 先分析关键的结点,本题中右移
k个位置,k可能大于链表的总长度,故实际只需要右移k%总长度即可。 - 假设
k = 2,则我们需要找到的关键结点有两个,第一个是倒数第k+1个位置3,右移后应该指向null,而3的下一个结点4应该成为新的头结点,第二个关键节点是结点5,移动后应该指向原来的头结点1。如图
注意:快慢指针同时走,快指针指向尾结点时,尾结点指的是fast.next = null的结点,而不是fast = null的结点,即图中的5,而不是null。此时,因为慢指针还要走n步才走到尾结点,所以慢指针指向的是倒数第n+1个结点,因为还要走n步才到尾结点,说明后面还有n个结点,所以它自身是倒数第n+1个结点,即倒数第n个结点的前一节点。这里说这么多,是因为这里需要理解记忆,之后便可以一次写对,而不是每次遇到链表问题时都去思考到底快慢指针指在哪个结点了。19.删除链表的倒数第N个结点 这篇文章有更为详细的描述,更好理解,所以强烈建议看完19题再看该题。

Java代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
//由于k可能大于链表的总长度,然后循环右移
//所以不妨先计算出总长度,然后右移(k%总长度)个位置
if(head == null) return head;
ListNode cur = head;
int count = 0;//记录总长度
while(cur != null){
count++;
cur = cur.next;
}
int n = k % count;
ListNode fast = head;
ListNode slow = head;
//快指针先走n步
for(int i = 0;i < n;i++){
fast = fast.next;
}
//快慢指针同时走,分别指向两个关键结点
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
//改变相关结点指向,实现最终目的
fast.next = head;
head = slow.next;
slow.next = null;
return head;
}
}

Go代码
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func rotateRight(head *ListNode, k int) *ListNode {
//由于k可能大于链表的总长度,然后循环右移
//所以不妨先计算出总长度,然后右移k%总长度个位置
if head == nil {
return nil
}
m := 0 // 链表节点总个数
cur := head
for cur != nil {
m++
cur = cur.Next
}
n := k % m // 需要右移n个节点
// 那么需要找到倒数第n个节点的前一节点,和尾节点两个关键节点,思路和19题删除倒数第n个节点一模一样了
// 快慢指针,注意:倒数第n个节点可能是第一个节点,又要找他的前一节点,所以设置一个虚拟头节点
dummy := &ListNode{}
dummy.Next = head
slow,fast := dummy,dummy
// 快指针先走n步
for i := 0;i < n;i++ {
fast = fast.Next
}
// 快指针最后指向了尾结点,慢指针指向了倒数第n个节点的前一节点,刚好是我们需要的两个关键节点
for fast.Next != nil {
fast = fast.Next
slow = slow.Next
}
// 如果slow等于dummy,说明根本不需要移动(k是m的整数倍的情况)
if slow == dummy {
return head
}
// 画个图就好理解了,尾节点需要连接头节点,然后倒数第n个节点成为新的头节点
fast.Next = head
head = slow.Next
slow.Next = nil
return head
}

本文详细介绍了如何使用快慢指针法解决链表旋转问题,例如给定链表按k个位置右移。通过分析关键结点,建立虚拟头结点,利用快慢指针寻找合适的位置并改变结点指向,实现链表的旋转。示例代码展示了具体实现过程。
573

被折叠的 条评论
为什么被折叠?



