「前端刷题」61. 旋转链表

185 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

题目

给你一个链表的头节点 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 <= 100
  • 0 <= k <= 2 * 109

思路

思路1

看到这道题时,我的第一个想法就是将原链表变成环链表,然后在正确的位置切断链表就ok了。

那我们怎么找到这个正确的位置呢?

首先我们需要知道原始链表的长度,然后通过原始链表的长度和要移动的次数,我们就可以找到要切断链表的位置。

注意事项

  • 考虑哪些情况我们不需要移动链表,直接返回原链表。
  • 在切断链表之前,一定要把需要被切断节点的后一个节点保存起来,作为新链表的第一个节点。

代码

/**
 * 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) {
    // k为0时不需要移动,链表为空或者链表只有一个节点直接返回链表
    if (k === 0 || !head || !head.next) return head;
    // 【第一步】建立锚节点,记录链表的头结点
    var dummy = new ListNode(0, head), cur = dummy;

    // 【第二步】计算出链表的长度
    let count = 0;
    // 循环执行完之后,cur指向了链表的最后一个位置
    while (cur.next) {
        cur = cur.next;
        count++;
    }
    // 如果链表的长度等于要移动的次数,直接返回链表
    if (k === count) {
        return head;
    } else if (k > count) {
        // 如果链表的长度小于要移动的位置数,直接对链表长度取模运算,
        // 因为如果链表的长度小于需要要移动的次数,意味着我们的环形链表至少需要循环一周以上
        k %= count;
    }

    // 将链表的最后一个位置,指向链表的第一个位置,形成一个环链表
    cur.next = dummy.next;
    // 将cur指向原链表的第一个位置
    cur = cur.next;

    // 【第三步】开始移动链表
    for (let i = 0; i < count - k - 1; i++) {
        cur = cur.next;
    }

    // 【第四步】移动完成,切断环形列表
    // 执行为for循环到这里之后,已经到了我们要切断链表的位置,但是在切断链表之前,
    // 将我们的锚点指向要切断链表的下一个位置,将其作为新链表的头结点
    dummy.next = cur.next;
    // 切断结点,这里一定不要忘记,否则就还是换链表
    cur.next = null;
    return dummy.next
};
时间复杂度:O(n)  --最多遍历两次链表
空间复杂度:O(1)  --常数个空间地址