单向链表反转的实现方法
https://www.zhihu.com/question/441865393/answer/3208578798
单向链表反转是数据结构中的经典问题,在面试和实际开发中经常遇到。以下是 多种实现方式(包括递归和迭代),以 Go 语言为例。
1. 单向链表结构定义
type ListNode struct {Val intNext *ListNode
}
2. 迭代法(推荐)
思路:
- 使用 三个指针
prev
、curr
、next
,逐个反转节点指向。 - 时间复杂度 O(n),空间复杂度 O(1)(仅用常量额外空间)。
代码实现
func reverseList(head *ListNode) *ListNode {var prev *ListNode // 前驱节点curr := head // 当前节点for curr != nil {next := curr.Next // 暂存下一个节点curr.Next = prev // 反转指向prev = curr // prev 后移curr = next // curr 后移}return prev // 返回新头节点
}
示例:
输入: 1 -> 2 -> 3 -> nil
输出: 3 -> 2 -> 1 -> nil
3. 递归法
思路:
- 递归到链表末尾,然后逐层反转指针。
- 时间复杂度 O(n),空间复杂度 O(n)(递归栈开销)。
代码实现
func reverseList(head *ListNode) *ListNode {if head == nil || head.Next == nil {return head // 递归终止条件}newHead := reverseList(head.Next) // 递归到末尾head.Next.Next = head // 反转指针head.Next = nil // 避免循环引用return newHead
}
执行过程:
reverseList(1):reverseList(2):reverseList(3): return 32.Next.Next = 2 → 3 -> 22.Next = nil → 3 -> 2 -> nil
1.Next.Next = 1 → 2 -> 1
1.Next = nil → 3 -> 2 -> 1 -> nil
4. 头插法(利用哑节点)
思路:
- 遍历链表,将每个节点插入到 哑节点(Dummy Node) 之后。
- 时间复杂度 O(n),空间复杂度 O(1)。
代码实现
func reverseList(head *ListNode) *ListNode {dummy := &ListNode{} // 哑节点for head != nil {next := head.Next // 暂存下一个节点head.Next = dummy.Next // 头插dummy.Next = head // 更新哑节点指向head = next // 移动 head}return dummy.Next
}
流程示例:
初始: dummy -> nil, 链表: 1 -> 2 -> 3 -> nil第1步: dummy -> 1 -> nil
第2步: dummy -> 2 -> 1 -> nil
第3步: dummy -> 3 -> 2 -> 1 -> nil
5. 对比总结
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
迭代法 | O(n) | O(1) | 大多数情况(推荐) |
递归法 | O(n) | O(n) | 代码简洁,但栈溢出风险 |
头插法 | O(n) | O(1) | 需要哑节点辅助 |
6. 测试用例
func printList(head *ListNode) {for head != nil {fmt.Print(head.Val, " -> ")head = head.Next}fmt.Println("nil")
}func main() {head := &ListNode{1, &ListNode{2, &ListNode{3, nil}}}printList(head) // 1 -> 2 -> 3 -> nilnewHead := reverseList(head)printList(newHead) // 3 -> 2 -> 1 -> nil
}
7. 常见问题
Q1:递归法的缺点?
- 链表过长时可能导致 栈溢出(Stack Overflow)。
- 递归调用有额外空间开销。
Q2:如何优化递归法?
- 某些编译器支持 尾递归优化(Tail Call Optimization, TCO),但 Go 目前不支持。
Q3:迭代法为什么更推荐?
- 无栈溢出风险,适合处理超长链表。
- 空间效率更高(O(1))。
掌握单向链表反转是理解指针操作的基础,建议手写实现并测试边界条件(如空链表、单节点链表)。