单链表翻转-链表篇

leetcode206单链表的翻转

题目:

给你单链表的头节点 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

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

思路
今天听左神的课,他说的一个观点蛮有意思:链表的题一律按笔试和面试练。笔试就是快速通过就行,而面试需要有较低的时间复杂度才能突出。

这题很容易想的就是用辅助栈,然后再把元素串起来,但空间复杂度是O(n)。

在题目最后说到了迭代和递归两种方法。迭代是利用旧值推出新值,是在函数内某段代码实现循环,递归是不断的调用函数自身实现循环。

1.双指针的方法(迭代法)

1->2->3->null 要变成 3->2->1->null

初始一个前指针pre为None,当前指针cur为head。遍历的时候,如要把2指向1,这时候2与3就会断开,所以先用after存放cur.next,然后把cur的指针指向pre。pre和next都往下移一个,pre=cur,cur=after。最后pre指向3,cur指向空。返回pre就是翻转后的单链表。

时间复杂度是O(n),空间复杂度是O(1)。

双指针代码

# 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: Optional[ListNode]) -> Optional[ListNode]:
        pre = None
        cur = head
        while cur:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        return pre
        

2.递归的方法

递归三要素,参数、终止条件和单层递归逻辑。

首先定义递归函数reverse(pre,cur),里面的参数传两个pre和cur。终止条件就是cur为空,返回pre。单层递归逻辑和双指针一样,先用一个变量after保存cur.next,然后cur指向pre。调用递归函数reverse(cur,after)。

在主函数中调用的时候pre为None,cur为head。

时间复杂度是O(n),空间复杂度是O(n)。空间复杂度取决于递归调用的栈空间,最多是n层。

递归代码:

# 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: Optional[ListNode]) -> Optional[ListNode]:
        def reverse(pre, cur):
            if not cur:
                return pre
            after = cur.next
            cur.next = pre
            return reverse(cur, after)
        return reverse(None, head)

92. 反转链表 II

题目:

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

在这里插入图片描述

示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]

提示:

链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

进阶: 你可以使用一趟扫描完成反转吗?

思路和代码

8月份刷的时候应该是没有刷出来,提交的代码是官网上的,再刷还是不会。思路有,代码细节处理不好(coding能力不行)。

思路一:

借用上一题的翻转,先遍历一次得到翻转前部分的链表+要翻转的链表部分+后半截链表。对要翻转的链表进行翻转之后再把三节串起来。

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        # 翻转单链表
        def reverse(head):
            pre, cur = None, head
            while cur:
                temp = cur.next
                cur.next = pre
                pre = cur
                cur = temp
        
        dummy = ListNode(-1)
        dummy.next = head
        start = dummy

        for _ in range(left-1):
            start = start.next
        
        cur = start
        for _ in range(right - left + 1):
            cur = cur.next
        
        # 得到需要翻转的链表 注意cur.next=None 把后半段割掉之后再翻转
        verse = start.next

        # 得到最后面的链表部分
        after = cur.next
        
        # 得到前一部分链表
        start.next = None

        cur.next = None

        # 翻转
        reverse(verse)

        # 三段链表连起来
        start.next = cur
        verse.next = after
        
        return dummy.next

思路二:

上面的方法如果left和right各在两边,那就需要两次遍历,下面这种方法一次遍历即可。

在这里插入图片描述
核心思想就是每遍历一个需要翻转的结点,就把这个结点查到要反转的第一个结点位置。例如遍历到5,就把5放在7后面。遍历到4的时候,就把4放到7的后面,那怎么做呢?

需要三个指针,如上图需要把5拉倒2前面(感觉图画的已经很清楚了)。

在这里插入图片描述

代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
        dummy = ListNode(-1)
        dummy.next = head
        pre = dummy
        for _ in range(left-1):
            pre = pre.next
        cur = pre.next
        
        for _ in range(right - left):
            after = cur.next
            cur.next = after.next
            # after.next = cur 错
            after.next = pre.next
            pre.next = after
        return dummy.next
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值