代码随想录算法训练营打卡day03-2|206 反转链表(双指针法、递归法)

学习来自《代码随想录》

建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。

题目链接/文章讲解/视频讲解:代码随想录

题目:力扣题目链接

视频:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

 206 反转链表(双指针法、递归法)

1 题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。不能申请额外的内存空间。

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]

输入:head = [] 输出:[]

2 题解

2.1 我的思路

✔️反转链表,只要将每个节点的next指针改成指向他的前一个元素,第一个元素指向NULL就可以了。

那么如何换向呢?

❌想到是遍历链表找到其最后一个元素和倒数第二个元素,开始改写node.next->前一个元素。

错误点:单链表无法回溯上一个节点,只能从头查找,元素无法后退,而且这样做会查找很多次。

所有要设计从头开始查找并从头就开始换向的方法,即方法一:双指针法。

2.2 方法一:双指针法

思路

1、反转链表,只要将每个节点的next指针改成指向他的前一个元素,第一个元素指向NULL就可以了。

2、定义一个cur指针,指向头结点,定义一个pre指针指向cur的上一个节点,初始化时指向NULL,这样头节点翻转变成尾节点时,尾节点指向null。每次循环,调换cur指向的节点的下一个节点指向方向。循环范围是cur=head开始,到cur=null为止。

由于cur调换指针方向后,与下一个节点断开联系,所以需要在调换方向前,用temp保存下一个节点,调换方向后pre和cur向后移动位置进入下一次循环。

代码

java版本

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode temp;

        while(cur != null){
            temp = cur.next; //先保存cur下一个节点
            cur.next = pre; //再改变cur下一个节点指向,断开原下个节点连接
            pre = cur;  //先向后移动pre
            cur = temp; //再移动cur
        }
        return pre;
    }
}

复杂度分析:

时间复杂度:O(n)

空间复杂度:O(1)

python 版本

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur = head   
        pre = None
        while cur:
            temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur.next = pre #反转
            #更新pre、cur指针
            pre = cur
            cur = temp
        return pre

2.3方法二:迭代法

思路

迭代法本质上是对方法一的改写,思想是一致的。代码简洁但是没有方法一的基础会比较难理解

代码

java版本

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverse(ListNode pre, ListNode cur){
        if(cur == null) return pre; //迭代出口
        ListNode temp = cur.next;
        cur.next = pre;
        //下一次迭代入参相当于把pre和cur都往后移一步
        return reverse(cur,temp);
    }
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);    
    }
}
  • 时间复杂度: O(n), 要递归处理链表的每个节点
  • 空间复杂度: O(n), 递归调用了 n 层栈空间

python版本

# Definition for singly-linked list.
//节点定义
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

//翻转链表
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        return self.reverse(head, None)
    def reverse(self, cur: ListNode, pre: ListNode) -> ListNode:
        if cur == None:
            return pre
        temp = cur.next
        cur.next = pre
        return self.reverse(temp, cur)

//测试程序
if __name__ == "__main__":
    head = None
    for i in[5,4,3,2,1]:
        head = ListNode(i, head)
    value1 = []
    temp = head
    while temp:
        value1.append(temp.val)
        temp = temp.next
    print(f'原链表:{value1}')
    print(head.val)

    result = Solution().reverseList(head)
    print(result.val)
    value2 = []
    temp = result
    while temp:
        value2.append(temp.val)
        temp = temp.next
    print(f'新链表:{value2}')

运行结果: 


疑问??:

前面用java写递归时,要注意pre和cur的后移顺序

public ListNode reverse(ListNode pre, ListNode cur){

...

return reverse(cur,temp);

}

等价于

pre = cur;

cur=temp

即移动pre和 cur时,先移动pre 再移动cur,如果先移动cur会丢失pre的移动目标。

但是写python 时,经过测试运行代码这样写得不到正确结果,链表还是原链表结果。要反过来写。

java和python 形参赋值时,都是从左到右。所有这里没有想明白为什么不是一致的。

3 总结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值