排序16:最短子数组

来源:互联网 发布:人工智能导论教学大纲 编辑:程序博客网 时间:2024/05/29 19:07

题目:对于一个数组,请设计一个高效算法计算需要排序的最短子数组的长度。给定一个int数组A和数组的大小n,请返回一个二元组,代表所求序列的长度。(原序列位置从0开始标号,若原序列有序,返回0)。保证A中元素均为正整数。测试样例:[1,4,6,5,9,10],6 返回:2

思路:这道题目的代码很简答,关键是对于题意的理解,所谓最短需排序子数组是指一个需要排序的最大区域,即题目中的数组只要找到一个待排序区域而不是分离的几段,如果在一个区域中元素逐步递增,即在遍历这个区域时,每个当前元素都是最大值max,那么说明这个区域是有序的;如果从某个元素开始,之后存在元素小于这个元素,那么这段区域就是非有序的,是需要排序的。为什么需要保留最大值max,在遍历时要保留最大值max,如果后面的每个元素都更大,则max会逐步向后面移动,因此max所在的区域都是有序的,如果在max后面出现了一个元素小于这个max,即没有替换这个max值,于是这个当前值到前面max所在的区域必然是需要重新排序的,此时用int right指针记录这个位置,之后再往后面移动,如果下一个元素大于max,则重新替换max,再往下移动,如果下一个元素小于max,则将right替换为当前值,说明这个元素之前需要重排序;一直按此进行,即只要值小于max,就应该赋值给right表示之前的部分是无需的,直到全部遍历完成,此时right所在的位置是应该排序的区域的右端点,在right及左边元素的区域是无序的。那么需要排序的区域的起始位置再哪里呢?应该是第一个小于max的元素对应的max所在的元素,即从某个元素开始如果元素小于了前面那么元素,说明开始无序,于是记录第一个right的位置start即可,区域开始的位置就是start-1;由于记录开始减少的第一个元素较麻烦,可以通过逆向遍历的方式来求无序数组的开始位置,以A[n-1]的值作为min,从i=n-2开始向前遍历元素,如果A[i]<min,那么就替换min,如果A[i]>min,说明出现了破坏数组有序性的元素,因此将该值赋值给left,于是从left所在位置向右的区域是需要重新排序的,以此方式向左遍历数组,当遍历完成时,left所在的位置就是需要重新排序的区域的第一个元素,于是最终[left,right]区域就是需要重新排序的区域,于是长度是right-left+1;注意理解,这里不必担心left>=right,因为2次遍历是对同一个数组进行的,因此结果一定是唯一统一的,如果存在无序的区域,那么一定是[left,right],如果不存在无序区域,即整个序列有序,那么必然有left=0;并且同时right=n-1,此时待排序部分长度是0,返回0即可。时间复杂度O(n),空间复杂度O(1)

import java.util.*;//注意理解题意,何为最短的待排序子数组:是连续的非有序区间public class Subsequence {    public int shortestSubsequence(int[] A, int n) {        //特殊输入        if(A==null||A.length<=0) return 0;        //从前往后遍历求出无序区域最右边的元素        int max=A[0];        int rightIndex=0;        for(int i=1;i<n;i++){            if(A[i]>=max){                //如果A[i]比前一个元素大,那么替换max,属于有序部分,不需要设置rightIndex                max=A[i];            }else{                //如果A[i]比前一个元素小,那么属于无序部分,需要设置rightIndex指针                rightIndex=i;            }        }        //从后往前遍历求出无序区域最左边的元素        int min=A[n-1];        int leftIndex=n-1;        for(int i=n-2;i>=0;i--){            if(A[i]<=min){                //如果A[i]比min小,说明向前是减小,属于有序序列,不需要设置leftIndex                min=A[i];            }else{                //如果A[i]比前一个元素大,那么属于无序部分,需要设置leftIndex指针                leftIndex=i;            }        }        //特殊的,如果原始数组有序,那么返回无序长度为0,这里使用&&或者||都一样因为同时满足或    者同时不满足;注意2个return之间的先后顺序不能调换        if(leftIndex==n-1&&rightIndex==0) return 0;        //已经求出leftIndex和rightIndex,显然可以求出区域的长度        return rightIndex-leftIndex+1;    }}

1 0
原创粉丝点击