LeetCode206 反转链表(带扩展)

83 阅读3分钟

leetcode.cn/problems/re…

image.png

解法一:迭代法

个人觉得迭代法是最直观易懂的,由于单链表的结构,至少要用三个指针才能完成迭代反转

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
    // 迭代法
    if head == nil || head.Next == nil{ // 空链表或者仅一个节点,无需反转
        return head
    }
    cur := head
    var nxt, pre *ListNode
    for cur != nil{
        nxt = cur.Next // 先记录下个节点位置
        // 执行反转
        cur.Next = pre
        // 更新指针,处理下个节点
        pre = cur
        cur = nxt
    }
    // 最后cur会一直遍历到链表结尾之外nil,而pre即为尾结点,也就是反转后链表的起点
    return pre
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

解法二:递归法

递归反转单链表的关键在于,这个问题本身是存在子问题结构的

例如,单链表 1->2->3->4,那么如果忽略这个头结点 1,只看 2->3->4 这个子链表,它也是个单链表对吧?那么,能不能先反转 2->3->4 这个子链表呢,然后再想办法把 1 接到反转后的 4->3->2 的最后面,是不是就完成了整个链表的反转?

reverseList(1->2->3->4) = reverseList(2->3->4) -> 1

reverseList 函数定义是这样的:输入一个节点 head,将「以 head 为起点」的链表反转,并返回反转之后的头结点

这就是「分解问题」的思路,通过递归函数的定义,把原问题分解成若干规模更小、结构相同的子问题,最后通过子问题的答案组装原问题的解。

最后的递归代码看起来非常简洁

    /**
     * Definition for singly-linked list.
     * type ListNode struct {
     *     Val int
     *     Next *ListNode
     * }
     */
    func reverseList(head *ListNode) *ListNode {
        if head == nil || head.Next == nil{
            return head
        }
        last := reverseList(head.Next)
        // 当链表递归反转之后,原本head.Next就会变为后半部分子链表的尾节点,那么它的next应该指向头节点
        head.Next.Next = head
        // 当链表递归反转之后,新的头结点是 last,而之前的 head 变成了最后一个节点,别忘了链表的末尾要指向 null
        head.Next = nil
        return last
    }
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

反转链表 II

image.png

解法一:迭代法

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseBetween(head *ListNode, left int, 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 *ListNode
    cur := p0.Next // 第left个节点
    for i := 1; i<=right-left+1; i++{
        nxt := cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }
    p0.Next.Next = cur
    p0.Next = pre
    return dummy.Next
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

解法二:抽象公共函数

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseBetween(head *ListNode, left int, right int) *ListNode {
    if head == nil || head.Next == nil{
        return head
    }
    if left == 1{ // 起点是头节点,反转前right个节点
        return reverseN(head, right)
    }
    // 找到第 left个节点的前驱
    pre := head
    for i:=1; i<left-1; i++{
        pre = pre.Next
    }
    // pre.Next即第 left个节点,以它为起点,反转前right-left+1个节点
    pre.Next = reverseN(pre.Next, right-left+1)
    return head
}

// 反转以head为起点的链表前n个节点
func reverseN(head *ListNode, n int) *ListNode{
    if head == nil || head.Next == nil{
        return head
    }
    cur := head
    var pre,nxt *ListNode
    for cur != nil{
        nxt = cur.Next
        // 执行反转
        cur.Next = pre
        // 更新指针
        pre = cur
        cur = nxt
        n--
        if n==0{
            break
        }
    }
    // 反转后pre是第n个节点(反转后的头节点),cur是第n+1个节点
    head.Next = cur
    return pre
}

另一种写法

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseBetween(head *ListNode, left int, right int) *ListNode {
    if head == nil || head.Next == nil{
        return head
    }
    if left == 1{ // 起点是头节点,反转前right个节点
        return reverseN(head, right)
    }
    // 找到第 left个节点的前驱
    pre := head
    for i:=1; i<left-1; i++{
        pre = pre.Next
    }
    // pre.Next即第 left个节点,以它为起点,反转前right-left+1个节点
    pre.Next = reverseN(pre.Next, right-left+1)
    return head
}

// 反转以head为起点的链表前n个节点
func reverseN(head *ListNode, n int) *ListNode{
    if head == nil || head.Next == nil{
        return head
    }
    var pre *ListNode
    cur, nxt := head, head.Next
    for i:=0; i<n; i++{
        cur.Next = pre
        pre = cur
        cur = nxt
        if nxt != nil{
            nxt = nxt.Next
        }
    }
    // 反转后pre是第n个节点(反转后的头节点),cur是第n+1个节点
    head.Next = cur
    return pre
}

参考

labuladong.online/algo/data-s…