题目在这: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