LeetCode61 旋转链表

48 阅读3分钟

leetcode.cn/problems/ro… image.png

解法一:反转链表

参考旋转数组这一题的解法,对于链表,向右轮转k个元素,其实就是分别把包含前 len-k个元素和后 k个元素的两个子链表交换位置,子链表内部元素要保持原来的顺序不变,具体分为三步:

  1. 反转整个链表
  2. 反转前k个节点
  3. 反转后len-k个节点

反转链表系列可以参考这篇题解

具体解法中,可能还涉及一些边界情况的特殊判断,例如:

  • 链表为空,无需旋转
  • 链表只有一个节点,无需旋转
  • k=0,那就是不旋转
  • k为len的整数倍,那就是旋转了n圈后回到原样,相当于不旋转
  • k > len,循环旋转的多圈可以略去,只需要考虑k%len的旋转次数
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func rotateRight(head *ListNode, k int) *ListNode {
    if head == nil || head.Next == nil || k == 0{
        return head
    }
    // 计算链表长度
    listLen := 0
    cur := head
    for cur != nil{
        listLen++
        cur = cur.Next
    }
    // 边界情况判断
    if k == listLen{
        return head
    }else if k > listLen{
        k = k%listLen
        if k == 0{
            return head
        }
    }
    // 迭代法反转整个链表
    var pre, nxt *ListNode
    cur = head
    for cur != nil{
        nxt = cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }
    // 反转后链表的头节点为pre
    revHead := pre
    // 反转前k个节点
    res := reverseBetween(revHead, 1, k)
    // 反转后len-k个节点
    res = reverseBetween(res, k+1, listLen)
    
    return res
}

func reverseBetween(head *ListNode, left, right int) *ListNode{
    if head == nil || head.Next == nil{
        return head
    }
    dummy := &ListNode{
        Next: head,
    }
    // 找到第 left个节点的前驱节点
    p0 := dummy
    for i:=0; i<left-1; i++{
        p0 = p0.Next
    }
    // 反转left到right这个区间
    var pre, nxt *ListNode
    cur := p0.Next // 第left个节点
    for i := 0; i<right-left+1; i++{
        nxt = cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }
    // p0是反转区间的前驱节点,p0.Next指向反转区间的第一个节点
    // cur此时走到了反转区间外的下一个节点
    // pre是区间反转后的第一个节点
    p0.Next.Next = cur
    p0.Next = pre
    return dummy.Next
}

解法二:闭合成环

旋转后新链表的最后一个节点为原链表的第len-k(准确来说是k%len)个节点,我们可以先将给定的链表连接成环,然后将指定位置断开

  1. 首先计算出链表的长度 n,并找到该链表的末尾节点,将其与头节点相连。这样就得到了闭合为环的链表
  2. 找到新链表的最后一个节点(即原链表的第len-k%len个节点),将当前闭合为环的链表断开,即可得结果
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func rotateRight(head *ListNode, k int) *ListNode {
    if head == nil || head.Next == nil || k == 0{
        return head
    }
    listLen := 1
    cur := head
    for cur.Next != nil{ // 遍历到最后一个节点
        listLen++
        cur = cur.Next
    }
    cur.Next = head // 尾节点指向头节点,连接成环
    pos := listLen - k%listLen // 新链表的尾节点位置
    // 即使k是 len的整数倍,这里计算得到pos为链表的最后一个节点,断开环后一样可以得到答案
    for pos > 0{
        cur = cur.Next
        pos--
    }
    newHead := cur.Next
    cur.Next = nil 
    return newHead
}