最短无序连续子数组
1.题目描述
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
示例 1:
输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
2.题解
2.1 暴力
public int findUnsortedSubarray(int[] nums) {
int res = nums.length;
for (int i = 0; i < nums.length; i++) {
for (int j = i; j <= nums.length; j++) {
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, prev = Integer.MIN_VALUE;
for (int k = i; k < j; k++) {
min = Math.min(min, nums[k]);
max = Math.max(max, nums[k]);
}
if ((i > 0 && nums[i - 1] > min) || (j < nums.length && nums[j] < max))
continue;
int k = 0;
while (k < i && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k != i)
continue;
k = j;
while (k < nums.length && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k == nums.length) {
res = Math.min(res, j - i);
}
}
}
return res;
}

数组[2, 6, 4, 8, 10, 9, 15]的最短无序连续子数组为nums[1:6],即[6, 4, 8, 10, 9]。
nums[0]和nums[6]是升序的。子数组nums[1:6]的最小值大于nums[0],最大值小于nums[6]。所以,只要对nums[1:6]这个子数组进行升序排序,那么整个数组都会变为升序排序。
解释代码:
通过两层循环遍历所有可能的子序列。
遍历到子序列[2, 6, 4]时,由于4<6,所以需要对[2, 6, 4]排序。
同理,遍历到子序列[2, 6, 4, 8, 10, 9]时,由于9<10,也需要对[2, 6, 4, 8, 10, 9]排序。
if ((i > 0 && nums[i - 1] > min) || (j < nums.length && nums[j] < max))
continue;
下面这部分代码用于检查nums[j:n−1]是否是升序的,如果nums[j:n−1]是升序的,那么k == nums.length,就可以求子数组nums[i:j]的长度。
int k = 0;
// ...
k = j;
while (k < nums.length && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k == nums.length) {
res = Math.min(res, j - i);
}
下面这部分代码用于检查nums[0:i−1]和nums[j:n−1]是否是升序的。
int k = 0;
while (k < i && prev <= nums[k]) {
prev = nums[k];
k++;
}
// ...
k = j;
while (k < nums.length && prev <= nums[k]) {
prev = nums[k];
k++;
}
if (k == nums.length) {
res = Math.min(res, j - i);
}
最后可以求得全局最短无序连续子数组。
2.2 更好的暴力
// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
int l = nums.length, r = 0;
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[i]) {
r = Math.max(r, j);
l = Math.min(l, i);
}
}
}
return r - l < 0 ? 0 : r - l + 1;
}
显然nums[0]在数组中的正确位置上。
当i = 1, j = 2时,由于nums[1]<nums[2](即6<4),所以nums[1]不在数组中的正确位置上,更新无序子数组的左边界为1,右边界为2。
当i = 4, j = 5时,同理,由于nums[4]<nums[5],更新无序子数组的左边界为1,右边界为5。
实际上,对于数组[2, 6, 4, 8, 10],对其子数组[6, 4]进行升序排序即可。而对于[2, 6, 4, 8, 10, 9],需要对其子数组[6, 4, 8, 10, 9]进行升序排序。
注意到对于数组[15, 4, 6, 8, 9, 10, 2],即使其子数组[4, 6, 8, 9, 10]是升序排序的,也需要对整个数组进行升序排序。
2.3 排序
// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
int[] snums = nums.clone();
Arrays.sort(snums);
int start = snums.length, end = 0;
for (int i = 0; i < snums.length; i++) {
if (snums[i] != nums[i]) {
start = Math.min(start, i);
end = Math.max(end, i);
}
}
return (end - start >= 0 ? end - start + 1 : 0);
}
对原数组进行升序排序后,对比原数组和排序后的数组,如果某个位置上的元素不相等,说明原数组中该位置的元素不在正确的位置上。
2.4 使用栈
// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
Stack < Integer > stack = new Stack < Integer > ();
int l = nums.length, r = 0;
for (int i = 0; i < nums.length; i++) {
while (!stack.isEmpty() && nums[stack.peek()] > nums[i])
l = Math.min(l, stack.pop());
stack.push(i);
}
stack.clear();
for (int i = nums.length - 1; i >= 0; i--) {
while (!stack.isEmpty() && nums[stack.peek()] < nums[i])
r = Math.max(r, stack.pop());
stack.push(i);
}
return r - l > 0 ? r - l + 1 : 0;
}
注意到对于数组[6, 2, 4, 8, 10, 15, 9],需要对整个数组进行升序排序,因为nums[0]和nums[6]都不在正确的位置上。
2.5 不使用额外空间
// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
boolean flag = false;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1])
flag = true;
if (flag)
min = Math.min(min, nums[i]);
}
flag = false;
for (int i = nums.length - 2; i >= 0; i--) {
if (nums[i] > nums[i + 1])
flag = true;
if (flag)
max = Math.max(max, nums[i]);
}
int l, r;
for (l = 0; l < nums.length; l++) {
if (min < nums[l])
break;
}
for (r = nums.length - 1; r >= 0; r--) {
if (max > nums[r])
break;
}
return r - l < 0 ? 0 : r - l + 1;
}
注意到nums[0]和nums[6]都在正确的位置上了,于是考虑子数组[6, 4, 8, 10, 9]。
由于6<4和10<9,所以min为4,max为10。
在无序子数组中,其最小元素应该放到该数组的第一个位置,其最大元素应该放到该数组的最后一个位置。
因此,第一个大于4的元素的位置是无序子数组的第一个位置,逆序遍历第一个小于10的元素的位置是无序子数组的最后一个位置。
以[2, 4, 9, 10, 8, 15]为例,由于10>8,min为8,max为10,第一个大于8的为9,逆序遍历第一个小于10的为8,所以[9, 10, 8]为最短无序连续子数组。
参考:

浙公网安备 33010602011771号