力扣(leetcode) 581. 最短无序连续子数组 (排序法) (双指针) 详细步骤解~~~你一看就会

题目在这:https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/

题目分析:

题目要求找的是一个子数组,排序该子数组后整个数组都将变成有序的。

显然数组中仅有一个这样的子数组,不会存在多个不连续的这样的子数组。
除了该子数组外,其他元素均为升序。

法一:(排序法)

思路分析

先说一个比较简单且容易理解的方法。
将所给数组排序,排序后和排序前进行索引比较。
不同位置的开始和结束就是我们要找的子数组的开始和结束。

比如 : 1,2,5,4,3,6,7 显然我们只需要将子数组 5,4,3 排序后,整个数组都将有序, 此时,对整个数组进行排序,变为:1,2,3,4,5,6,7.
然后我们比较排序前后的索引。

找元素 1 所在位置的索引 : 排序前 0 号位,排序后 0号位
找元素 2 所在位置的索引 : 排序前 1 号位,排序后 1号位
找元素 3 所在位置的索引 : 排序前 4 号位,排序后 2号位
找元素 4 所在位置的索引 : 排序前 3 号位,排序后 3号位
找元素 5 所在位置的索引 : 排序前 2 号位,排序后 4号位
找元素 6 所在位置的索引 : 排序前 5 号位,排序后 5号位
找元素 7 所在位置的索引 : 排序前 6 号位,排序后 6号位

我们从前往后遍历的第一个索引不同的位置,和从后往前遍历的第一个不同索引的位置,就是子数组的尾和头。

对于上面这个例子的头和尾 就是 2,4。

我们使用left和right指针指向头和尾。

最后输出的时候要注意 空数组和只有一个元素数组的情况,就是left和right都指向同一位置的情况,自然返回right-left。其他情况返回right-left+1

完整代码

class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        left = 0
        right = 0

        new_nums = sorted(nums)
        for i in range(len(nums)): # 从前往后遍历,找尾
            if nums[i] != new_nums[i]:
                left = i
                break
        for j in range(len(nums)-1,0,-1): # 从后往前遍历,找头
            if nums[j] != new_nums[j]:

                right = j
                break


        if left == right: 
            print(right - left)
            return right - left
        else:
            print(right-left+1)
            return right - left +1

法二: (双指针法)

思路分析:

在这里插入图片描述

数组被分为 A, B, CA,B,C 三段,且有:

  • A,C 都是升序序列。
  • B 中任一元素均大于 A 中元素,且小于 C 中任一元素。

这里以寻找C 的边界 r( B 的右边界)为例,上面分析过,C 中元素必须全部大于 B 中元素,也即 C 中的最小值大于等于 C 之前全部元素的最大值。那么从右往左,第一个不满足这个条件的元素就是边界。我们利用 mx[i] 数组记录 [0, i - 1][0,i−1] 区间内元素的最大值,利用 mn[i] 数组记录 [i, n - 1][i,n−1] 区间内元素的最小值。那么如果我们从后往前遍历,找到的第一个 mn[i] < mx[i] 的位置 i,就是 B,C 之间的边界。

比如: 1,2,5,4,3,6,7 .
首先我们正向,从左到右遍历。1,2 ,5 都是升序,
5到4位降序,此时更新right指针,指向4。
再到3时, 又是降序,指针更新指向3,
到6为升序,不变。
最终right指向 3.
同理反向遍历,从右到左。找到left指针即可。

完整代码

class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        left, right = -1, -1
        max_num = nums[0]
        min_num = nums[-1]

        for i in range(len(nums)): # 正向找右边边界 right
            if nums[i] >= max_num: # 处于升序阶段,顺序无误
                max_num = nums[i] # 更新当前最大值
            else: # 出现了降序,顺序有误,更新右边界
                right = i

        for j in range(len(nums)-1,-1,-1) : # 反向 找左边界 left
            if nums[j] <= min_num: # 处于升序阶段,顺序无误
                min_num = nums[j] # 更新当前最小值
            else: # 出现了降序,顺序有误,更新左边界
                left = j


        return (right - left +1) if (left != -1) else 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值