【单向链表】
61. 旋转链表
旋转链表,将链表的每个节点向右移动 k
个位置。
本题题目意思清晰,实现起来比较容易,属于链表基础知识题。
简单题我们仔细做,通过这道简答题来回顾一下单向链表的一些基本操作。
在进行具体的旋转链表操作之前,我们要先统计一下链表的长度 n
,为什么?因为要 “将链表的每个节点向右移动 k
个位置”,如果 k >= n
,我们只需要将链表的每个节点向右移动 k % n
个位置即更新 k %= m
,k / n
表示要将链表的每个节点向右移动整数个 n
位置,把长度为 n
的链表向右移动 n
个位置等于没有动。
如何求链表的长度 n
?通过迭代枚举单向链表中的所有节点,计数得到链表的长度。具体地,初始化 n = 0
,从头结点出发枚举节点,枚举到一个节点 ++n
,接着更新当前节点为下一个节点,直到遇到空节点表示链表迭代枚举结束,退出。
将链表的每个节点向右移动 k
个位置,那么原链表的倒数第 k
个节点就是移动后链表的最后一个非空节点,也就是空节点的前一个节点,我们需要找到该节点,然后连上空节点。并且还要将原链表的最后一个非空节点连接到原链表的头结点,这才算完成移动操作(也就是题目说的选择链表)。
在代码实现中,我们先实现的是连接头节点(也就是 官方题解 中说的闭合成环),于是需要遍历原链表找到最后一个非空节点,这个我们可以在计算链表长度的时候将原链表的最后一个非空节点记录下来。然后将移动后链表的最后一个非空节点连接到空节点上,通过从 head
节点往后迭代 n - k - 1
次即可得到移动后链表的最后一个非空节点 lastNode
,而 lastNode->next
节点就是我们最终要返回的向右移动后的新链表的头节点。
实现代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head == nullptr || head->next == nullptr) {
return head;
}
// 计算链表长度
ListNode* curr = head;
int n = 1;
while(curr->next != nullptr) {
++n;
curr = curr->next;
}
k %= n;
if(k == 0) {
return head;
}
// 连接头结点
curr->next = head; // 注意此行代码放置的位置
// 找出移动后链表的最后一个非空节点
n -= k;
lastNode = head;
while(--n) {
lastNode = lastNode->next;
}
// 建立新的头结点
ListNode* newHead = lastNode->next;
lastNode->next = nullptr;
return newHead;
}
};
复杂度分析
时间复杂度: O ( n ) O(n) O(n), n n n 是链表的长度,最坏情况下,我们需要遍历该链表两次。
空间复杂度: O ( 1 ) O(1) O(1),我们只需要常数的空间存储若干变量。
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head or not head.next:
return head
n = 1
cur = head
while cur.next:
cur = cur.next
n += 1
k %= n
if k == 0:
return head;
cur.next = head
cur = head
n -= k
n -= 1
while n:
cur = cur.next
n -= 1
newHead = cur.next
cur.next = None
return newHead
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 哦。