LeetCode-581-最短无序连续子数组

题目描述:给你一个整数数组 nums ,你需要找出一个连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。请你找出符合题意的最短子数组,并输出它的长度。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

重点:使用单调栈维护整个数组顺序,如果出现退栈的操作,说明存在子字符串的顺序并不是正确的,用start_index记录此时出栈的元素索引的最小值(乱序子字符串的起始位置),并用end_index记录出栈元素索引的最大值,但这个并不是乱序字符串的终止位置。稍后会分情况说明

少啰嗦,先看东西:

def findUnsortedSubarray(nums):
    mon_stack = list()
    import sys
    start_index = sys.maxsize
    end_index = -sys.maxsize
    max_num = -sys.maxsize

    for i in range(len(nums)):
        # 经过所有的进栈和退栈操作,可以定位无序字符串的起始位置
        # 基本定位无序字符串的结束位置,不精确
        while mon_stack and nums[mon_stack[-1]] > nums[i]:
            pop_index = mon_stack.pop()
            start_index = min(start_index, pop_index)
            end_index = max(end_index, pop_index)
        mon_stack.append(i)
    # 没有退栈操作,说明字符串有序,直接返回0
    if start_index == sys.maxsize and end_index == -sys.maxsize:
        return 0
    max_num = max(nums[start_index:end_index+1])
    # 在栈中的所有元素都是有序的,即存在end_index+1:len(nums)范围的子字符串有序
    # 如果当前位置元素并不满足当前位置应该具有的大小要求,同样要调整顺序
    for i in range(end_index+1, len(nums)):
        if nums[i] < max_num:
            end_index += 1
        else:
            break
    
    return end_index - start_index + 1
第一种情况:从小到大顺序字符串

如:nums = [1, 2, 3, 4, 5]
结果:0
过程:nums中元素不断进栈,全程无退栈操作,所以start_indexend_index 都是默认值,直接 return 0

前置知识:对于从小到大的字符串,对任意位置i∈[0, len(nums)-1]nums[i]>=max(nums[:i])nums[:i]意为nums从索引0到索引i-1的所有元素

第二种情况:从大到小顺序字符串

如:nums = [5, 4, 3, 2, 1]
结果:5
过程:

  1. 5进栈
  2. 5退栈,4进栈,此时,start_index=0记录元素5的索引,end_index=0记录元素5的索引
  3. 4退栈,3进栈,此时,start_index不改变,因为元素4的索引并不比start_index变量的值小,end_index=1修改为元素4的索引
  4. 3退栈,2进栈,此时,start_index不改变,因为元素3的索引并不比start_index变量的值小,end_index=2修改为元素3的索引
  5. 2退栈,1进栈,此时,start_index不改变,因为元素2的索引并不比start_index变量的值小,end_index=3修改为元素2的索引

此时,还有元素1残留在栈中。我们把元素1当作nums从索引4到数组结尾的有序子字符串,那么由前置知识可以知道,我们要判断元素1与之前所有元素的最大值的大小,不满足说明元素1的顺序也是错误的,很显然,元素1<max值5,所以end_index + 1 = 4
6. 最终结果为end_index - start_index + 1 = 5

第三种情况:部分有序字符串

其实第三种情况与第二种相同,只不过第三种情况的有序子字符串长度比1大,第二种情况可以看作是有序子字符串长度为1的特殊情况。
如: nums = [1, 2, 3, 4, 2, 3, 4]
结果:4
过程:

  1. 1进栈
  2. 2进栈
  3. 3进栈
  4. 4进栈
  5. 4退栈,此时,start_index=2记录元素4的索引,end_index=2记录元素4的索引
  6. 3退栈,此时,start_index=1修改为更小的元素3的索引,end_index不改变
  7. 2进栈
  8. 3进栈
  9. 4进栈
    此时栈内元素残存的都是有序的,虽然元素1和元素2也在栈内,但是它们一定小于start_index-end_index无需范围的最小值。
    反证:如果start_index前面的元素大于无需范围的元素,则在无序范围元素进栈时,该元素一定会退栈,但直到最后它们都在栈内,说明假设错误。
    计算得到start_index-end_index范围元素的最大值为4,遍历(end_index+1)-len(nums)范围的元素:
  10. 元素2<4,end_index + 1 = 3
  11. 元素3<4,end_index + 1 = 4
  12. 元素4 == 4,break,不再向下循环
  13. return end_index - start_index + 1 = 4 - 1 + 1 = 4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值