题目描述
给定一个链表,将链表向右循环移动 $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
算法
(模拟,链表) $O(n)$
这道题中 $k$ 可能很大,所以我们令 $k = k\%n$,$n$是链表长度。
创建两个指针first, second
,分别指向头结点,先让first
向后移动 $k$ 个位置,然后first
和second
同时向后移动,直到first
走到链表最后一个元素。
此时first
指向链表末尾,second
指向分界点。然后我们把链表从分界点处断开,然后把后半段接在前半段前面即可。
时间复杂度分析:链表一共遍历2遍,所以总时间复杂度是 $O(n)$。
C++ 代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (!head) return head;
int n = 0;
ListNode *p = head;
while (p)
{
n ++ ;
p = p->next;
}
k %= n;
if (!k) return head;
ListNode *first = head;
while (k -- && first) first = first->next;
ListNode *second = head;
while (first->next)
{
first = first->next;
second = second->next;
}
first->next = head;
head = second->next;
second->next = 0;
return head;
}
};
这里因为要保证k比n小,所以先遍历了一遍已经求出了n,后面没必要再用双指针了吧
用不用都可以,不影响时间复杂度。
y总, head = second->next;这句话啥意思
此时second->next是新的根节点,链表题画画图就好了。
y总,这道题没有定义虚拟头结点吧?我看你直接用的
head
变量。这道题没有定义虚拟头结点啊。
但是你题解写的是
指向虚拟头结点
hhh可能是写题解的时候用了虚拟头结点,后来发现没必要,就在代码里删掉了hh
当知道了链表的长度时,用双指针是否没有意义了呢,不是可以直接从
head
进行 n - k 次next
操作吗。也可以,这两种做法的时间复杂度是一样的。
谢谢
这题也可以用环形链表来做吧
可以的,先把开头结尾接起来,再在某个地方把链表断开
first->next = head;这句话啥意思,大佬
把链表的前半段接在链表结尾
为啥k很大就要%n
代码中first指针会走 $k$ 步,当 $k$ 很大时就超时了
避免不必要的运算?
是的
能不能举个例子,什么时候会出现边界问题?
这题没有边界问题,题解和代码已更新hh
我的复杂度位O(kn)但是速度超越100%,好奇怪
估计是代码写得好,或者数据比较小hh