题目链接
这个题一开始想用双指针,用left和right移动,想的是遇到第一个逆序的位置就是left,其实这只是对题目中的2 6 4 8 10 9成立,假如说是个2 4 8 6 10,按照一开始的算法会把6的位置作为最左端,显然不对。
但是一开始雏形的思想中还是有一点正确的部分的,对2 6 4 5 10 12 11,记录left是6的位置后,5不仅要和4比,也要和6比,比4大比6小right就要更新到5,而处理10 12之后时,只要都比left大,就只需要保证之后的是递增的,遇到11时发生逆序right就要更新…
上述思路进行不下去就是因为没有利用到有序,只想相邻的元素比较看是否逆序,而且想一次性的确定出left和right,事实证明一次性还是很困难的,在一次循环中分两次确定也是一种方法。
查看官方题解后,重新整理一下思路:
将子数组排序之后整个数组就有序了,说明原数组可以分为ABC三部分,B部分[left,right]是导致原数组无序的,AC两部分分别有序,那么对A中每个数字,它肯定小于等于BC两部分中的每个数字,也就是小于等于BC中数字的最小值minn,最左端不能满足这个条件的位置就是left的位置,为了O(1)得到BC数字最小值,从i=n-1开始往小遍历,遇到比minn更小的num[i]就将minn更新,反之就更新left为此时的i。
同理,对C中的每个数字,他肯定大于等于AB中的每个数字,也就是大于等于AB中数字的最大值maxn,最右端不能满足这个条件的位置就是right的位置,为了O(1)得到AB中数字的最大值,从i=0开始往大遍历,遇到比maxn更大的num[i]就将maxn更新,反之就更新right为此时的i。
反思以上过程,其实用minn和maxn就完美的填充的自己一开始的思路中既要和这个比又要和前面的比的思想,这个利用的是ABC中每个数字关系的性质,其实一开始的思路中确定left之后就认为此时的left就是一个最值,后面一旦遇到比他小的则right就要更新,但是对6 10 9就需要多开情况10 9逆序的更新,如果记录的是6 10中的最大值,让9与之比较从而更新right到9,这样就统一起来了,本质还是利用上述ABC的关系。
总之吸取的教训就是,不一定非要一次性考虑确定left和right,寻找数之间的规律。
当然还有更容易想的就是先排序,然后对照原数组找到不同的部分,不再赘述。
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
int left=-1,minn=0x3f3f3f3f;
int right=-1,maxn=-0x3f3f3f3f;
int len=nums.size();
for(int i=0;i<len;i++){
if(nums[i]>=maxn){
maxn=nums[i];
}else{
right=i;
}
if(nums[len-i-1]<=minn){
minn=nums[len-i-1];
}else{
left=len-i-1;
}
}
int res=(right==-1)?0:right-left+1;
return res;
}
};