61. 旋转链表

246 阅读2分钟

题目描述

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k **个位置。

分析

输入:链表头节点 head 输出:经过移动后的,新的头节点

解题思路

这题我采取的是闭合为环的思路,也就是先把这个链表的 tailhead 连接 🔗 起来,形成一个环, 然后根据 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)

image.png

可以看到,我要返回的是 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),存储几个指针