题目描述
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k **个位置。
分析
输入:链表头节点 head
输出:经过移动后的,新的头节点
解题思路
这题我采取的是闭合为环的思路,也就是先把这个链表的 tail 和 head 连接 🔗 起来,形成一个环,
然后根据 k, 在适当的位置断开,再 return 新的 head,就可以解决这个问题。
那么首先是把链表变成环,在连接的过程中,我也计算了链表的长度,方便后续找到返回的 head。
let cur = head;
let len = 1;
function linkTailToHead() {
while (cur.next) {
cur = cur.next;
len++;
}
// 注意⚠️,我的 len 初始值是1,因为一开始就是 head,而且对于空链表,我会在代码开始直接做边界条件的处理,不会往下执行
// 因为我取的是 cur.next 作为 while 条件,因此最终 cur 是 tail,让他的 next (本来应该是 null)去指向 head,就可以得到环
cur.next = head;
}
然后一步很关键啊,是我们根据 k,找到要返回的 head,以及链表断开的位置,我们看张 leetCode 的图(k = 2)
可以看到,我要返回的是 4,那就得在 3 断开,因为 3 是 tail,这块只能通过观察大法得到一个规律:
首先,如果链表移动次数刚好是链表长度,那就不用动了,直接返回 head,所以需要对传入的 k 取一个模,得到实际上应该走的步数。
然后,我们要得到 3 的话,如果从 1 走两步,链表长度是 5,k 是 2 那么 head 需要从 1 走链表长度 len, 减去 k 就是 3,那实际上我们要从一个 dummyHead 开始计算,所以 3 步刚好。
ret = dummyHead
for (let i = 0; i < len - (k % len); i++) {
ret = ret.next; // ret 最终指向 3
}
// 存储 head
const tmp = ret.next;
// tail.next = null
ret.next = null;
最终返回 head,也就是 ret。
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var rotateRight = function (head, k) {
if (!head) return head;
const dummyHead = new ListNode(null, head);
let len = 1,
ret = dummyHead;
let cur = head;
linkTailToHead();
function linkTailToHead() {
while (cur.next) {
cur = cur.next;
len++;
}
cur.next = head;
}
for (let i = 0; i < len - (k % len); i++) {
ret = ret.next;
}
const tmp = ret.next;
ret.next = null;
// 如果根本没动,也就是 k === len,把做的环断开就好了
if (ret === dummyHead) cur.next = null;
return tmp;
};
复杂度
时间:O(N),我们需要循环最多两遍链表 空间:O(1),存储几个指针