zoukankan      html  css  js  c++  java
  • 最短无序连续子数组

    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<410<9,所以min4max10
    在无序子数组中,其最小元素应该放到该数组的第一个位置,其最大元素应该放到该数组的最后一个位置。
    因此,第一个大于4的元素的位置是无序子数组的第一个位置,逆序遍历第一个小于10的元素的位置是无序子数组的最后一个位置。
    [2, 4, 9, 10, 8, 15]为例,由于10>8min8max10,第一个大于8的为9,逆序遍历第一个小于10的为8,所以[9, 10, 8]为最短无序连续子数组。

    参考:

  • 相关阅读:
    线性表单链表的实现
    线性表顺序存储结构
    【C语言】产生随机数
    TCP/IP协议
    【bfs】奇怪的电梯(P1135)
    【DFS】取数游戏(P1123)
    【DFS】圣诞夜的极光(P1454)
    【贪心】骑士的工作(P2695)
    【贪心】纪念品分组(P1094)
    生活的那么一点反思
  • 原文地址:https://www.cnblogs.com/gzhjj/p/14190046.html
Copyright © 2011-2022 走看看