最短子数组

来源:互联网 发布:台湾网络电视tv版 编辑:程序博客网 时间:2024/06/13 00:31

最短子数组

问题描述:

  给定一个数组a和数组长度n,求出需要排序的最短子数组长度,使得该子数组排好序时整个数组有序。

测试样例:

  • 输入:a={1,4,6,5,9,10}, n=6
  • 输出:2

问题分析:

  由题意知,我们需要找到一个子数组使得当该子数组排好序时整个数组有序。这就是说原数组可以分为三个部分,即a=pqk,此时我们找到的子数组为q。p和r已经分别有序且p中所有元素小于q中任意一个元素,k中任意一个元素大于q中所有元素。
  为了做到这点,我们可以简单的枚举出所有的子数组q(长度从小到大),然后按照上面的思路进行验证。当枚举到某个子数组q时,该子数组前面的元素有序且小于q中任意一个元素,需要时间开销:

|p|+|p|×|q|
若对其优化,只用|p|中最大值验证是否小于|q|中元素,需要时间开销为:
|p|+|q|
该子数组后面的元素有序且这些元素中任意一个元素都大于q中所有元素,需要时间开销:
|k|+|q|×|k|
若对其优化,只用|k|中最小值验证是否大于|q|中元素,需要时间开销:
|k|+|q|
则子数组q的长度即为所求,此时每一次处理需要的时间开销大致为:
|q|×(|p|+|k|)=|q|×(n|q|)
优化后大致为:
|p|+|q|+|k|=n
所以这种方法的时间复杂度是:
|q|=1n|q|(n|q|)2

优化后为:
|q|=1nn(n|q|)=n3n2(n+1)2
即O(n^3)级别。
  为了能获得更好的时间开销算法,我们可以对数组进行两次扫描。第一次从左往右扫描找到最大值不变的最后一个位置r(注意扫描时最大值在不断被更正),对于上述测试样例,该策略的处理步骤是:
  首先最大值是1,r为0;接下来最大值是4,此时最大值改变了,也就是说r不用变;同理最大值是6时r也不变;当扫描到5时由于最大值没有改变,故r更新为3;之后最大值一直在改变,所以r最终等于3。
  第二次从右往左扫描找到最小值不变的最后一个位置l,对于上述测试样例,l的值为2。所以p={1,4},q={6,5},k={9,10},此时q的长度为2,故返回2。显然这种算法的时间复杂度为O(n),比第一种枚举策略更高效。
  下面给出在java代码下的实现:

    int shortestSubsequence(int[] A, int n) {        if(A==null || n<=0)            return 0;        int i, j, l, r;        j = A[0];        r = 0;        for (i = 1; i < n; i++) {            if (A[i] < j)                r = i;            else                j = A[i];        }        if (r == 0)            return 0;        j = A[n - 1];        l = n - 1;        for (i = n - 2; i >= 0; i--) {            if (A[i] > j)                l = i;            else                j = A[i];        }        return r - l + 1;    }
1 0
原创粉丝点击