题目
解法一:优化的暴力算法(基于选择排序算法思想上)
- 主要思想
基于选择排序算法的基础上,通过二重循环比较i、j位置的元素,i从0开始,j从i+1开始,i<j<nums.length,我们可以从头遍历每一个数组元素(nums[i]),然后通过它与下一个的位置元素(nums[j])进行比较,如果该元素比nums[i]元素小,则表示nums[i]与nums[j]的位置不是升序排序的,再继续比较nums[j] 的下一个元素,在所有的 nums[i] 中,我们找到最左边不在正确位置的 nums[i] ,这标记了最短无序子数组的左边界(L)。类似的,我们找到最右边不在正确位置的边界 nums[j],标记最短无序子数组的右边界 (r),然后通过(L-r+1)得到该数组排序不正确的长度。
- 算法实现
class FindSort{
//解法一:基于选择排序的优化暴力算法
public int findSortTest01(int[] nums) {
int L = nums.length;
int r = 0;
for(int i=0;i<nums.length;i++) {
for(int j=i+1;j<nums.length;j++) {
if(nums[i]>nums[j]) {
L = Math.min(L, i);
r = Math.max(r, j);
}
}
}
return r-L>0?r-L+1:0;
}
}
-
结果
-
复杂度
时间复杂度:O(n^2) 。使用了两重循环。
空间复杂度:O(1) 。只使用了常数空间。
解法二:排序解法
- 主要思想
复制一个数组,然后与原来的数组对应比较,记录开始元素不相等时的位置和最后元素不相等时的位置。
- 算法实现
class FindSort{
//解法二:排序
public int findSortTest02(int[] nums){
int[] snums = nums.clone();
Arrays.sort(snums);
int start = snums.length;
int 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);
}
}
-
结果
-
复杂度
时间复杂度:O(n log n)
空间复杂度:O(1)
解法三:使用栈(基于选择排序算法思想上)
- 主要思想
该方法背后思想仍然是选择排序。使用栈从头遍历数组,如果是升序则把数组元素的下标压栈,否则把栈顶元素出栈(返回的是下标),记录左标记L;然后从数组的尾部开始遍历,与上述同理,然后记录右标记r。
图解
- 算法实现
class FindSort{
//解法三:使用栈
public int findSortTest03(int[] nums) {
Stack<Integer> stack = new Stack<>();
int L = nums.length;
int 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 j=nums.length-1;j>=0;j--) {
while(!stack.isEmpty() && nums[stack.peek()] < nums[j]) {
r = Math.max(r, stack.pop());
}
stack.push(j);
}
return r-L>0?r-L+1:0;
}
}
-
结果
-
复杂度
时间复杂度:O(n)。需要遍历数组一遍,栈的时间复杂度也为 O(n)。
空间复杂度:O(n)。栈的大小最大达到 n
解法四:不使用额外的空间
- 主要思想
这个算法背后的思想是无序子数组中最小元素的正确位置可以决定左边界,最大元素的正确位置可以决定右边界。首先,从头开始遍历数组,如果遇到不是升序的数组位置,则用min记录该位置最小元素;再逆序遍历数组比较数组元素,遇到不是降序的数组位置则用max记录该位置最大元素;最后通过从头遍历数组与最小元素min进行比较,如果比它大,后面的元素则不需要比较了,因为前面已经比较过了,直接用L记录该元素的位置为左边界;逆序找出右边界(r)的方法与前面类似。
- 算法实现
class FindSort{
//解法四:不使用额外的空间
public int findSortTest04(int[] nums) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int i = 1; i<nums.length;i++) { //正序比较
if(nums[i]<nums[i-1]) {
min = Math.min(min, nums[i]); //记录不是升序时位置为最小元素
}
}
for(int j = nums.length-2;j>=0;j--) { //逆序比较
if(nums[j]>nums[j+1]) {
max = Math.max(max, nums[j]); //记录不是降序时位置为最大元素
}
}
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?r-L+1:0;
}
}
- 结果
- 复杂度
时间复杂度:O(n),使用了4个for循环
空间复杂度:O(1),使用了常数空间
测试算法
- 测试
package leetCodeTest01;
import java.util.Arrays;
import java.util.Stack;
/**
*最短无序连续子数组
* @author 卟淡
*
*/
public class FindsortSubarryTest {
public static void main(String[] args) {
FindSort s = new FindSort();
int[] nums = {2,6,4,8,10,9,15};
System.out.println("----解法一-----");
long t1 = System.currentTimeMillis();
int len01 = s.findSortTest01(nums);
System.out.println(len01);
long t2 = System.currentTimeMillis();
System.out.println("需要的时间:" + (t2-t1));
//其它方法同理
}
}
参考:LeetCode官方解答