题目
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
示例:
输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
提示:
0 <= len(array) <= 1000000
分析
假设这个有序是 升序。
以[1,2,4,7,10,11,7,12,6,7,16,18,19]为例子。
其实就是找逆序对。图中范围就是逆序对[7,10,11]和[12,6,7]的范围。
我们找到左边第一逆序值和右边最后一个逆序值的位置就是需要的范围了。
从左到右
最后一个逆序值是array[9]位置的7。
从右到左
最后一个逆序值是 array[3]的位置7。
所以需要修改的最小区间是[3,9]。
代码实现
- 从左往右找到最后一个逆序值的位置
- 从左到右扫描 ,记录最大值。
- 如果当前值小于最大值,那么就是逆序值。记录它的位置。继续扫描。
- 如果当前值大约最大值,更新最大值。继续扫描。
- 知道整个数组扫描结束。记录的位置就是最右边的逆序值的位置。
// 从左往右
// 默认第一个值是最大值。
int max = array[0];
//记录最右边逆序值的位置
int right= -1;
//开始扫描
for (int i = 1; i < array.length; i++) {
if(array[i]>=max){
max = array[i];
}else{
right = i;
}
}
- 从右往左找到最后一个逆序值的位置
- 从右往左扫描,记录最小值
- 当前扫描值大于最小值,那么就是一个逆序值,记录它的位置。继续扫描。
- 当前扫描值小于最小值,更新最小值。继续扫描。
- 知道整个数组扫描结束。记录的位置就是最左边的逆序值的位置。
//从右往左
//默认最后一个值是最小值
int min = array[array.length - 1];
//记录最左边逆序值的位置
int left = -1;
//开始扫描
for (int i = array.length - 2; i >= 0; i--) {
if (array[i] <= min) {
min = array[i];
} else {
left = i;
}
}
整个代码是
/**
* 给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。
* 注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],
* 若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
* <p>
* 示例:
* <p>
* 输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
* 输出: [3,9]
* 提示:
* <p>
* 0 <= len(array) <= 1000000
* <p>
* 来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/problems/sub-sort-lcci
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class 面试题_16_16_部分排序 {
public int[] subSort(int[] array) {
if (array.length == 0) return new int[]{-1, -1};
// 从左往右
// 默认第一个值是最大值。
int max = array[0];
//记录最右边逆序值的位置
int right = -1;
//开始扫描
for (int i = 1; i < array.length; i++) {
if (array[i] >= max) {
max = array[i];
} else {
right = i;
}
}
//说明是升序了,后面可以结束。
if (right == -1) {
return new int[]{-1, -1};
}
//从右往左
//默认最后一个值是最小值
int min = array[array.length - 1];
//记录最左边逆序值的位置
int left = -1;
//开始扫描
for (int i = array.length - 2; i >= 0; i--) {
if (array[i] <= min) {
min = array[i];
} else {
left = i;
}
}
return new int[]{left, right};
}
}
运行结果