最短子数组

最短子数组

问题描述:

  给定一个数组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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值