最短无序连续子数组 | 算法详细分析

题目:最短无序连续子数组。

给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。

示例 1:

    输入: [2, 6, 4, 8, 10, 9, 15]
    输出: 5
    解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

说明 :

​ 输入的数组长度范围在 [1, 10,000]。
​ 输入的数组可能包含重复元素 ,所以升序的意思是<=。

 package com.test.day6_9;
 
 /**
 
  * @author cosefy
 
  * @date 2020/6/10
    */
    public class FindUnsortedSubarray {
    public static void main(String[] args) {
        int[] nums = {2, 5, 2, 3};
        int res1 = findUnsortedSubarray_Test1(nums);
        int res3 = findUnsortedSubarray_Test3(nums);
 
    ​    System.out.println("The result of test1: " + res1);
    ​    System.out.println("The result of test3: " + res3);
    }
解法一:暴力算法(不占用空间)

思路:一边从左边开始,依次确定当前元素是否是当前位置以后最小的元素,否则就退出循环,得到左边界;同理,获得右边界。
分析:时间复杂度(n),空间复杂度O(1)
易错点:注意参数的边界问题
思考:
-时间效率不高,可以考虑用空间换时间

public static int findUnsortedSubarray_Test1(int[] nums) {
    int front_count = 0;
    int rear_count = 0;
    int i;
    for (i = 0; i < nums.length; i++) {
        boolean flag1 = true;
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] > nums[j]) {
                flag1 = false;
                break;
            }
        }
        if (flag1)
            front_count++;
        else
            break;
    }
    for (int i2 = nums.length - 1; i2 >= i; i2--) {
        boolean flag2 = true;
        for (int j2 = i2 - 1; j2 >= i; j2--) {
            if (nums[i2] < nums[j2]) {
                flag2 = false;
                break;
            }
        }
        if (flag2)
            rear_count++;
        else
            break;
    }
    return nums.length - front_count - rear_count;
}
解法二:先排序再确定边界

思路:排序之后,存放到一个新数组中,通过比较两个数组的差异得到左右边界。
分析:时间复杂度O(nlogn),空间复杂度O(n)

解法三:确定左右两边局部边界,再判断

思路:首先根据数组左边符合递增排序确定简单左边界,根据数组右边符合递增排序确定右边简单右边界。接下来,找出数组无序部分的最小值和最大值,对左右两个简单边界进行精确边界的判断。
分析:时间复杂度O(n),空间复杂度O(1)
易错点:注意循环的判断条件,注意取值

  public static int findUnsortedSubarray_Test3(int[] nums) {
       int left = 0;
       int right = nums.length - 1;
       while (left < right) {
           if (nums[left] < nums[left + 1])   //符合递增条件
               ++left;           //简单左边界右移
           else
               break;            //不满足条件则跳出循环
       }
       while (left < right) {
           if (nums[right] > nums[right - 1])
               --right;
           else
               break;
       }
       //上面两个循环得到左右两个初级边界

   ​    int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
   ​    for (int i = left; i < right; i++) {
   ​        min = Math.min(min, nums[i]);   //获得当前无序序列中的最小值
   ​        max = Math.max(max, nums[i]);   //获得当前无序序列中的最大值
   ​    }

   ​    //下面根据min和max对初级边界进行精确缩减
   ​    for (int i = 0; i <= left; i++) {
   ​        if (nums[i] > min) {
   ​            left = i;
   ​            break;
   ​        }
   ​    }
   ​    for (int i = nums.length - 1; i >= right; i--) {
   ​        if (nums[i] < max) {
   ​            right = i;
   ​            break;
   ​        }
   ​    }
   ​    return right - left + 1;
   }

}
原文地址:https://www.cnblogs.com/cosefy/p/13084468.html