206. 反转链表

来源:力扣(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
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值