数组中最短无序序列

题目描述:

给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意:n-m越小越好,也就是说,找出符合条件的最短序列。

示例:

输入:1, 2, 4, 7, 10, 11, 7, 12, 6, 7, 16, 18, 19
输出:(3, 9)


解法:
题目的要求是要找出两个索引,这表明数组中间有一段数据有待排序,其中数组开头和末尾是排好序的。容易想到的是,直接找出位于开头的最长递增子序列,以及位于末尾的最长递增子序列。于是:

左边:1, 2, 4, 7, 10, 11
中间:7, 12
右边:6, 7, 16, 18, 19

很容易就能找出这些子序列,只需从数组最左边和最右边开始分别扫描,向中间查找递增子序列。一旦发现有元素大小顺序不对,那就是找到了递增子序列的两端。但是,为了解决这个问题,还需要对数组中间部分进行排序,只要将中间部分排好序,数组所有元素便是有序的。具体来说,就是一下判断条件必须为真:

/* 左边(left)所有元素都要小于中间(middle)的所有元素*/
min(middle) > end(left)

/* 中间(middle)所有元素都要小于右边(right)的所有元素*/
max(middle) < start(right)

但是在上面的例子中可以看出,min(middle) > end(left)或max(middle) < start(right)并不一定总能满足。因此,需要缩减左边和右边的子序列,直到先前的条件成立为止。

令min等于min(middle),max等于max(middle)。
对于左边部分:我们先从子序列的末尾开始(值11,索引为5),并向左移动,直至找到元素索引i使得aray[i] < min;找到后只需排序中间部分,就能让数组的那部分有序。

对于右边部分:进行类似左边的操作,此时max等于12,,我们先从右边子序列的起始元素(值为6)开始,并向右移动,将中间部分的最大值12依次与6,7,16比较。找到16时,就能确定16的右边已经没有元素比12小了(因为右边是递增子序列)。至此,对数组中间部分进行排序,以使整个数组都是有序的。

下面是算法的实现代码:

public class PartialSortedSeq {

    public static void main(String[] args) {
        PartialSortedSeq partialSortedSeq = new PartialSortedSeq();
        int[] arr = {1,2,7,4,10,11,7,12,6,7,16,18,19};
        partialSortedSeq.findUnsortedSequene(arr);
    }

    public void findUnsortedSequene(int[] arr) {
        /*找出左连续子序列*/
        int end_left = 0;
        for(int i = 1; i < arr.length; i++) {
            if (arr[i] > arr[i-1]) {
                end_left = i;
            } else {
                break;
            }
        }

        /*找出右连续子序列*/
        int start_right = arr.length - 1;
        for(int i = arr.length-2; i >= 0; i--) {
            if (arr[i] < arr[i+1]) {
                start_right = i;
            } else {
                break;
            }
        }

        /*找出中间部分的最大值和最小值*/
        int min_index = end_left + 1;
        int max_index = start_right - 1;
        for(int i = min_index+1; i <= start_right-1; i++) {
            if (arr[i] < arr[min_index]) {
                min_index = i;
            }
            if (arr[i] > arr[max_index]) {
                max_index = i;
            }
        }

        int left_index = 0;
        /*向左移动,直到小于arr[min_index]*/
        for(int i = end_left; i >= 0; i--) {
            int comp = arr[min_index];
            if (arr[i] >= comp) {
                left_index = i;
            } else {
                break;
            }
        }

        int right_index = arr.length - 1;
        /*向右移动,直到大于arr[max_index]*/
        for(int i = start_right; i < arr.length; i++) {
            int comp = arr[max_index];
            if (arr[i] <= comp) {
                right_index = i;
            } else {
                break;
            }
        }

        System.out.println(left_index + "," + right_index);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值