一日一练: 旋转链表

239 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

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

1. 题解

将链表移动k个位置,需要注意的有两点

  1. k大于链表长度时: 比如下图的链表,k可以是2也可以是7,只要k满足 链表长度len * N + 2旋转结果都是一样的。所以要知道对于任意k,链表实际旋转的最小位置是多少。

image.png

  1. 当进行旋转操作时,如果把要旋转的部分看成一个组,剩下的看一个成组,就很类似链表中两个节点的反转。主要是找到几个点的位置:

    • 因为可能会修改头节点的位置,需要哨兵节点: 要链接旋转区域的头节点

    • 要旋转区域的前一个节点pre

      • 找到旋转区域的头节点pre.next
      • 防止形成有环链表,需要置为null
    • 最后一个节点last: 链接原来的头节点

image.png

根据上图,代码就很好写了:

function rotateRight(head: ListNode | null, k: number): ListNode | null {
    // 特殊情况:如果为null,或者只有一个节点,或者移动位置为0都直接返回
    if (head === null || head.next === null || k === 0) return head
    let cur = head
    let len = 0
    // 计算总长度
    while(cur) {
        len++
        cur = cur.next
    }    
    // 真实移动的位置
    const realRotateOffset = k % len
    // 如果为0,还是直接返回
    if (realRotateOffset === 0) {
        return head
    }
    // 哨兵节点
    const dummy = new ListNode(-1, head)
    let preOffset = len - realRotateOffset
    let pre = dummy
    while(preOffset-- > 0) {
        pre = pre.next
    }  
    let next = pre.next
    // 防止产生有环列表 上图中的第三条连接线
    pre.next = null
    let last = next
    while(last && last.next !== null) {
        last = last.next
    }    
    // last -> dummy.next 上图中的第二条连接线
    last.next = dummy.next
    // dummy --> pre.next 上图中的第一条连接线
    dummy.next = next
    return dummy.next
};

2. 复杂度

  • 时间复杂度: 循环了两次, O(N)
  • 空间复杂度:缓存几个变量的数据,O(1)