Leetcode 581. Shortest Unsorted Continuous Subarray

题目

Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order, too.You need to find the shortest such subarray and output its length.

Example 1:
Input: [2, 6, 4, 8, 10, 9, 15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.

Note:
Then length of the input array is in range [1, 10,000].The input array may contain duplicates, so ascending order here means <=.

解法1:改进的brutal force(TLE)

对于这道题的brutal force的方法是,找到任何可能的nums[i:j],判断是否这个区间是否为所搜索的区间,这种方法的复杂度为O(n^3)。这边使用一种改进的brutal force方法。这种方法是类似与选择排序的思想:

  • 对于任何可能的区间[i,j],j>i,如果nums[i]>nums[j]的话,说明这两个数字在错误的位置,所以I就是left边界的candidate,j就是right边界的candidate
  • 我们需要寻找left边界candidate中的最小值和right边界中的最大值
  • 这个最大值和最小值形成的区间就是我们要寻找的最小交换区间
class Solution(object):
    def findUnsortedSubarray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        left = n
        right = 0
        for i in range(n-1):
            for j in range(i+1,n):
                if nums[i] > nums[j]:
                    right = max(right,j)
                    left = min(left,i)
        return right-left+1 if right>left else 0

时间复杂度:O(n^2)
空间复杂度:O(1)

解法2:利用排序

  • 将nums进行排序,将排序后的数组和原数组进行比较
  • 先从左端开始遍历,找到第一个原数组与排序数组不一样的元素位置
  • 再从有段开始遍历,找到第一个原数组与排序数组不一样的元素位置
  • 从左往右后从右往左分别找到的第一个元素分别为我们要寻找的左右边界
class Solution(object):
    def findUnsortedSubarray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """ 
        copy_nums = copy.deepcopy(nums)
        copy_nums.sort()
        right = left = 0
        for i in range(len(nums)):
            if nums[i] != copy_nums[i]:
                left = i
                break
        for i in range(len(nums)-1,-1,-1):
            if nums[i] != copy_nums[i]:
                right = i
                break
        return right-left+1 if right>left else 0

时间复杂度:O(nlogn)+O(n) = O(nlogn)
空间复杂度:O(n)

C++版本

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        vector<int> tmp = nums;
        sort(tmp.begin(),tmp.end());
        int count = 0;
        int p1 = 0;
        while(p1 < nums.size() && nums[p1] == tmp[p1]){
            p1++;
        }
        int p2 = nums.size()-1;
        while(p2 >= 0 && nums[p2] == tmp[p2]){
            p2--;
        }
        if(p2 <= p1){
            return 0;
        }else{
            return p2-p1+1;
        }
    }
};

解法3:利用单调栈

  • 首先从左向右遍历数组,将碰到的升序元素push进入stack,一旦遇到比前一个元素小的元素,说明这个位置在错误的位置
  • 接下来需要找到这个错误的元素原本正确的位置,而这个正确的位置就是区间的左边界
  • 寻找这个正确位置的方法是,将stack依次pop元素,直到pop到比错误元素小的元素,那么这个元素index的下一个就是错误元素的正确位置,也就是我们要寻找区间的左边界
  • 同样的,从右向左可以找到区间的右边界
class Solution(object):
    def findUnsortedSubarray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """ 
        l = len(nums)-1
        r = 0
        stack = []
        stack.append(0)
        for i in range(1,len(nums)):
            while stack and nums[stack[-1]]>nums[i]:
                l = min(l,stack.pop())
            stack.append(i)
        stack = []
        stack.append(len(nums)-1)
        for i in range(len(nums)-1,-1,-1):
            while stack and nums[stack[-1]]<nums[i]:
                r = max(r,stack.pop())
            stack.append(i)
            
        return r-l+1 if r>l  else 0

时间复杂度:O(n)
空间复杂度:O(n)

二刷单调栈

核心思想是维护单调栈,寻找左右边界
从左边开始,维护一个单调递增栈,寻找错误index的最小值。从右边开始,维护一个单调递减栈,寻找错误index的最大值

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        stack<int> st1;
        int l = INT_MAX,r = 0,tmp;
        for(int i=0;i<nums.size();i++){
            while(!st1.empty() && nums[st1.top()] > nums[i]){
                tmp = st1.top();
                st1.pop();
                l = min(l,tmp);
            }
            st1.push(i);
        }
        stack<int> st2;
        for(int i=nums.size()-1;i>=0;i--){
            while(!st2.empty() && nums[st2.top()] < nums[i]){
                tmp = st2.top();
                st2.pop();
                r = max(r,tmp);
            }
            st2.push(i);
        }
        return r - l > 0 ? r - l + 1 : 0;
    }
};

解法4:利用常数空间

这个方法的主要思想是,在没有没正确排序的数组中,最小元素的正确位置为所寻找区间的左边界,最大元素的正确位置为所寻找区间的右边界

  • 从左向右遍历,一旦找到错误元素,则证明没有正确排序的序列肯定在这之前就已经开始,这时候从这向后的所有元素中找到最小的元素
  • 同样的,从右向左找到最大的错误元素
  • 再次从左向右和从右向左遍历,找到最大元素和最小元素的正确位置,这两个正确位置就是我们要寻找的区间
class Solution(object):
    def findUnsortedSubarray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """ 
        _min = float('inf')
        _max = float('-inf')
        n = len(nums)
        flag = False
        for i in range(1,n):
            if nums[i]<nums[i-1]:
                flag = True
            if flag:
                _min = min(_min,nums[i])
        flag = False
        for i in range(n-2,-1,-1):
            if nums[i]>nums[i+1]:
                flag = True
            if flag:
                _max = max(_max,nums[i])
        for l in range(n):
            if _min<nums[l]:
                break
        for r in range(n-1,-1,-1):
            if _max>nums[r]:
                break
        return r-l+1 if r>l else 0

时间复杂度:O(n)
空间复杂度:O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值