来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list
给你单链表的头节点 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
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
解题思路:
1、简单粗暴方式就是重新构造链表,遍历链表的所有节点并存储至一个栈中,然后依次取值并重新修改当前指针的Next指针,缺点就是需要多出一倍的存储空间。
2、双指针迭代方式,next 指针存储当前节点的下一个节点,就是 next = curr.Next,prev 指针存储前一个节点,curr 指针指当前节点,然后修改当前节点的下一个节点为前一个节点 就是 curr.Next = prev,然后当前指针指向未修改前的下一个节点,就是 curr = next,然后再将 prev 指针移动到当前指针上,然后依次挪动到链表尾部。因为 curr 最后指向空时,会退出,所以返回队尾的 prev 指针就是翻转指针的开始部分,因此 prev 最开始需要指向空节点。
对应golang版本代码为
/**
* 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
}
3、双指针递归方式。我们假设链表一共有 m 个节点,即 n1/n2/.../nk/n(k+1)/...nm (数字 1/2/k/k+1/m 是n的下标,表示第 k 个元素,k从1取到m),假设nk后面均已做了翻转,就是 nk 指向了 nk + 1,nk+1 下一个指针为空,nk+2 指向的是nk+1,因此需要修改 nk+1的下一个指针指向nk,nk的下一个指针指向nk+1的指针也就是空。递归时也就是函数的入栈操作,传递时,每次入栈均传递当前节点的下一跳节点做入栈即可,然后修改当前节点下一跳的Next指针为当前节点,当前节点更改为空即可。入栈完成后,函数返回需要将链表的尾部的节点返回,也就是新的表头最为返回值。
讲人话就是我把当前节点记录后,又调用当前的函数传入了当前节点的下一个节点,然后下一个节点记录后,有将下一个节点的下一个节点传入了当前的函数。虽然是同一个函数,但是却是功能相同的的函数在另一块内存空间里做了存储。然后将当前节点的下一个节点的下一跳指向当前节点,当前节点指向空。然后将最后的节点作为返回值即可。
对应的golang版本代码为:
/**
* 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
}
// 上一个函数兄弟传回来了翻转后的链表表头,
// 我这得记录一下,等我处理完我就传递一下。
new_head := reverseList(head.Next)
// 我不知道我是众多递归调用函数中的哪一段,
// 但是我只需要把我知道的当前节点的下一个节点的下一跳
// 指向当前节点就行。
head.Next.Next = head
// 当前节点的下一跳下一个函数也会修改,
// 我这改不改都行,但是万一当前的我是出栈后的最后一个节点,
// 我不修改的话,会出现闭环,那干脆指向空,免的出现闭环。
// 也就是 A.next = B,B.Next = A ,遍历时就会出现无限套娃。
// 所以那我就做个截断吧。
head.Next = nil
// 处理完了,把上一个函数传过来的表头传递出去,
// 免得最后一个哥们无法告知入口在哪里。
return new_head
}