题目来源
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
}
};
题目描述
给定一个无序数组arr,求出需要排序的最短子数组的长度,对子数组排序后能使得整个数组有序,即为需要排序的数组。例如:arr=[1,5,3,4,2,6,7]返回4,因为只有[5,3,4,2]需要排序。
题目解析
题意分析
因此,我们只需要找到左边第一个无序元素,右边第一个无序元素即可
分析
为了容易理解,我们从O(nlogn)出发来推导O(n)方案
方案一:O(nlogn)方案
- 把整个序列重新排序一下。显然的,只有挪窝的数字才需要排序。
1 5 3 4 2 6 7
1 2 3 4 5 6 7 - 由于只能连续子数组一同排序,所以答案是以最左、左右需要挪窝数字为左右端点的数组。
方案二:O(n)
- 方案一有一个问题,就是我们引入了排序却没有用到相对次序,这可能会浪费了算力。
那么有没有办法避免排序呢? - 有的,对于第i个元素,如果它满足下列公式,那么它显然不必要挪窝嘛。
max(arr[0…i)<=arr[i]<=max(arr[i…n]) - 换过来说,如果需要挪窝,那么显然不满足这个条件不是?
so,两个for解决
对于方案二,还可以这样想
- 我们可以假设把这个数组分成三段,左段和右段是标准的升序数组,中段数组虽是无序的,但满足最小值大于左段的最大值,最大值小于右段的最小值。
- 那么我们目标就很明确了,找中段的左右边界。分两边开始遍历
- 从左到右维护一个最大值max,在进入右段之前,那么遍历到的nums[i]都是小于max的,我们要求的end就是遍历中最后一个小于max元素的位置;
- 同理,从右到左维护一个最小值min,在进入左段之前,那么遍历到的nums[i]也都是大于min的,要求的begin也就是最后一个大于min元素的位置。
思路
- 从左往右遍历,维护一个max,为划过部分的最大值
- 初始时max = arr[0],i从1开始遍历
- arr[i] > max,(破坏单调性),×,当前值需要移动到前面去
- arr[i] <= max,(单调性不变),√
- 并记录最右边画X的位置,这个位置就是要排序的右边界
- 从右往左遍历,维护一个min,为划过部分的最小值
- 初始时max = arr[n-1],i从n-2开始遍历
- arr[i] < max,(破坏单调性),×,当前值需要移动到后面去
- arr[i] >= max,(单调性不变),√
- 并记录最左边画X的位置,这个位置就是要排序的左边界
举个例子
- 从左到右遍历:
- 从右到左遍历
因此,需要排序的个数是:
问题:只进行一趟排序不行吗?为什么一定要两边排序
我们举个反例
如果只一趟排序,那么8没有人管
实现
int getMinLength1(std::vector<int> arr){
int N = arr.size();
if(N <= 1){
return 0;
}
int lmax = arr[0];
int idx1 = -1;
for (int i = 1; i < N; ++i) {
if(lmax > arr[i]){
idx1 = i;
}else{
lmax = arr[i];
}
}
if(idx1 == -1){
return 0;
}
int rmin = arr[N - 1];
int idx2 = -1;
for (int i = N - 2; i >= 0; --i) {
if(rmin < arr[i]){
idx2 = i;
}else{
rmin = arr[i];
}
}
return idx1 - idx2 + 1;
}
对数器
int getMinLength2(std::vector<int> arr){
int N = arr.size();
if(N <= 1){
return 0;
}
int max = arr[0];
int nomaxIndex = -1;
for (int i = 1; i != N; ++i) {
if(arr[i] < max){
nomaxIndex = i;
}else{
max = std::max(max, arr[i]);
}
}
if(nomaxIndex == -1){
return 0;
}
int min = arr[N - 1];
int nominIndex = -1;
for (int i = N - 2; i != -1; --i) {
if(min < arr[i]){
nominIndex = i;
}else{
min = std::min(min, arr[i]);
}
}
return nomaxIndex - nominIndex + 1;
}
std::default_random_engine e;
void generateRandom(int maxLen, int minValue, int maxValue, std::vector<int> &arr){
std::uniform_int_distribution<int> distS(1, maxLen);
std::uniform_int_distribution<int> distV(minValue, maxValue);
std::uniform_real_distribution<double> distf;
int size = distS(e);
arr.resize(size);
for (int i = 0; i < size; ++i) {
arr[i] = distV(e);
}
}
int main() {
e.seed(time(NULL));
int maxLen = 100, minValue = -1000, maxValue = 100000;
for (int i = 0; i < 100000; ++i) {
std::vector<int> arr;
generateRandom(maxLen, minValue, maxValue, arr);
if(getMinLength1(arr) != getMinLength2(arr)){
printf("error\n");
return 0;
}
}
printf("ok\n");
return 0;
}