用go实现-反转链表

目录

题目

算法思路

方法一:迭代法

方法二:递归法

代码

方法一:迭代法

方法二:递归法

完整测试代码

代码详解

方法一:迭代法详解

迭代法执行过程图示

方法二:递归法详解

递归法执行过程图示

边界情况处理

空链表处理

单节点链表处理


题目

206. 反转链表

简单

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

复制代码
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

复制代码
输入:head = [1,2]
输出:[2,1]

示例 3:

复制代码
输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

**进阶:**链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

算法思路

方法一:迭代法

思路

使用三个指针:prev、curr、next,逐个反转节点的指向。

方法二:递归法

思路

递归到链表末尾,然后从后往前反转指针。

代码

方法一:迭代法

/**

* Definition for singly-linked list.

* type ListNode struct {

* Val int

* Next *ListNode

* }

*/

// 迭代法

func reverseList(head *ListNode) *ListNode {

var prev *ListNode

curr := head

for curr != nil {

// 保存下一个节点

next := curr.Next

// 反转指针

curr.Next = prev

// 移动指针

prev = curr

curr = next

}

return prev

}

方法二:递归法

// 递归法

func reverseListRecursive(head *ListNode) *ListNode {

// 递归终止条件

if head == nil || head.Next == nil {

return head

}

// 递归反转剩余部分

newHead := reverseListRecursive(head.Next)

// 反转当前节点

head.Next.Next = head

head.Next = nil

return newHead

}

完整测试代码

Go 复制代码
package main

import "fmt"

type ListNode struct {
    Val  int
    Next *ListNode
}

// 迭代法
func reverseList(head *ListNode) *ListNode {
    var prev *ListNode
    curr := head
    
    for curr != nil {
        next := curr.Next
        curr.Next = prev
        prev = curr
        curr = next
    }
    
    return prev
}

// 递归法
func reverseListRecursive(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    
    newHead := reverseListRecursive(head.Next)
    head.Next.Next = head
    head.Next = nil
    
    return newHead
}

// 创建链表
func createList(nums []int) *ListNode {
    if len(nums) == 0 {
        return nil
    }
    
    head := &ListNode{Val: nums[0]}
    current := head
    
    for i := 1; i < len(nums); i++ {
        current.Next = &ListNode{Val: nums[i]}
        current = current.Next
    }
    
    return head
}

// 打印链表
func printList(head *ListNode) {
    current := head
    for current != nil {
        fmt.Printf("%d", current.Val)
        if current.Next != nil {
            fmt.Printf(" -> ")
        }
        current = current.Next
    }
    fmt.Println()
}

func main() {
    // 测试用例1
    fmt.Println("=== 测试用例1 ===")
    head1 := createList([]int{1, 2, 3, 4, 5})
    fmt.Print("原链表: ")
    printList(head1)
    
    reversed1 := reverseList(head1)
    fmt.Print("迭代法反转: ")
    printList(reversed1)
    
    // 测试用例2
    fmt.Println("\n=== 测试用例2 ===")
    head2 := createList([]int{1, 2})
    fmt.Print("原链表: ")
    printList(head2)
    
    reversed2 := reverseListRecursive(head2)
    fmt.Print("递归法反转: ")
    printList(reversed2)
    
    // 测试用例3
    fmt.Println("\n=== 测试用例3 ===")
    var head3 *ListNode = nil
    fmt.Print("原链表: ")
    printList(head3)
    
    reversed3 := reverseList(head3)
    fmt.Print("反转后: ")
    printList(reversed3)
}

代码详解

方法一:迭代法详解

复制代码
func reverseList(head *ListNode) *ListNode {
    // prev 指向已反转部分的头节点,初始为nil(因为还没有任何节点被反转)
    var prev *ListNode
    // curr 指向当前要处理的节点,从头节点开始遍历
    curr := head
    
    // 遍历整个链表,直到当前节点为nil(链表末尾)
    for curr != nil {
        // 关键步骤1:保存下一个节点的引用
        // 因为下一步要修改curr.Next,需要提前保存next指针,否则会丢失后续节点
        next := curr.Next
        
        // 关键步骤2:反转指针方向
        // 让当前节点指向已反转部分的头节点,完成反转操作
        curr.Next = prev
        
        // 关键步骤3:移动prev指针
        // prev向前移动,指向当前节点(现在成为已反转部分的新头节点)
        prev = curr
        
        // 关键步骤4:移动curr指针  
        // curr向前移动,指向之前保存的下一个节点,继续处理
        curr = next
    }
    
    // 循环结束后,prev指向原链表的尾节点,也就是反转后的新头节点
    return prev
}

迭代法执行过程图示

以链表 1 → 2 → 3 → nil 为例:

初始状态

复制代码
prev: nil
curr: 1 → 2 → 3 → nil

第1次循环

复制代码
next = 2           // 保存节点2
1.Next = nil       // 1 → nil  
prev = 1           // prev指向1
curr = 2           // curr指向2
当前链表:nil ← 1    2 → 3 → nil

第2次循环

复制代码
next = 3           // 保存节点3
2.Next = 1         // 2 → 1
prev = 2           // prev指向2  
curr = 3           // curr指向3
当前链表:nil ← 1 ← 2    3 → nil

第3次循环

复制代码
next = nil         // 保存nil
3.Next = 2         // 3 → 2
prev = 3           // prev指向3
curr = nil         // curr指向nil
当前链表:nil ← 1 ← 2 ← 3

最终结果:返回 prev=3(新头节点)

方法二:递归法详解

复制代码
func reverseListRecursive(head *ListNode) *ListNode {
    // 递归终止条件:
    // 1. head == nil:空链表,直接返回nil
    // 2. head.Next == nil:到达最后一个节点,该节点就是反转后的新头节点
    if head == nil || head.Next == nil {
        return head
    }
    
    // 递归调用:反转以head.Next为头节点的子链表
    // newHead 始终指向原链表的尾节点,也就是反转后的新头节点
    newHead := reverseListRecursive(head.Next)
    
    // 关键步骤1:让当前节点的下一个节点指向当前节点
    // 这相当于在反转的链表中建立从后向前的连接
    // 例如:如果head是2,head.Next是3,那么让3.Next指向2
    head.Next.Next = head
    
    // 关键步骤2:断开当前节点原来的指向,防止形成环状链表
    // 当前节点的Next指针置为nil,因为它在反转后的链表中应该是尾节点
    head.Next = nil
    
    // 返回新头节点,这个newHead在递归过程中一直传递,始终指向原链表的尾节点
    return newHead
}

递归法执行过程图示

以链表 1 → 2 → 3 → nil 为例:

递归调用栈

复制代码
reverse(1)
  reverse(2)
    reverse(3) → 返回3(终止条件)

递归返回过程

reverse(3)返回

复制代码
直接返回3(因为3.Next == nil)

reverse(2)返回

复制代码
head = 2, newHead = 3
执行: 2.Next.Next = 2  → 3.Next = 2  (3 → 2)
执行: 2.Next = nil     → 断开2→3的连接
当前链表:3 → 2 → nil
返回: 3

reverse(1)返回

复制代码
head = 1, newHead = 3  
执行: 1.Next.Next = 1  → 2.Next = 1  (2 → 1)
执行: 1.Next = nil     → 断开1→2的连接
当前链表:3 → 2 → 1 → nil
返回: 3

边界情况处理

空链表处理

复制代码
// 两种方法都天然处理空链表情况
if head == nil {
    return nil  // 直接返回nil
}

单节点链表处理

复制代码
// 递归法通过终止条件处理
if head.Next == nil {
    return head  // 单节点链表反转后还是自身
}

// 迭代法在循环中处理,单节点链表循环一次就结束
相关推荐
席万里3 小时前
关于Go的init函数执行顺序#黑魔法
开发语言·网络·golang
小年糕是糕手5 小时前
【C++】C++入门 -- 输入&输出、缺省参数
c语言·开发语言·数据结构·c++·算法·leetcode·排序算法
程序猿小白日记7 小时前
走向智能化:从编程语言看人工智能的未来
leetcode
hweiyu009 小时前
数据结构:链表
数据结构·链表
喵了几个咪9 小时前
Golang微服务框架kratos实现Socket.IO服务
开发语言·微服务·golang
天选之女wow10 小时前
【Hard——Day8】65.有效数字、68.文本左右对齐、76.最小覆盖子串
linux·运维·redis·算法·leetcode
2501_9418008811 小时前
5G技术引领下的智能制造革命:如何推动工业4.0发展
leetcode
小白程序员成长日记11 小时前
2025.11.21 力扣每日一题
算法·leetcode·职场和发展
海奥华212 小时前
分库分表技术详解:从入门到实践
数据库·后端·mysql·golang