Leetcode P0154"Find Minimum in Rotated Sorted Array II" 题解

2020/09/17 Leetcode

博文中会简要介绍Leetcode P0154题目分析及解题思路。

“Find Minimum in Rotated Sorted Array II”这道题是p0153和p0081结合的变形题。

在p0081中我们要寻找特定的target并且数组中有重复。在p0153里特定的target是整个数组中的最小值但数组无重复。

本题是将这两个题目相结合。同样虽然我们无法得知最小值,但是我们依旧可以通过考虑mid与数组两端、mid两边邻居的关系来判断最小值在左半边还是右半边区间,从而二分搜索。

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]> ).

Find the minimum element.

The array may contain duplicates.

Example 1:

Input: [1,3,5]
Output: 1

Example 2:

Input: [2,2,2,0,1]
Output: 0

Note:

  • This is a follow up problem to Find Minimum in Rotated Sorted Array(p0153).
  • Would allow duplicates affect the run-time complexity? How and why?

这道题需要解决的核心问题在于当前mid大于等于左邻,小于等于右邻时会出现同时和左右相等的情况。这种情况下,最小值可以出现在mid左边或右边,所以需要两边搜索,此时几乎无法采用迭代的思路,而递归的解法更加简洁明了。

除此之外,这道题的follow-up中提问时间复杂度的变化,答案是最坏情况下会变为O(n),平均时间复杂度还是O(logn),原因是有一个情况需要两边都搜索,譬如最坏情况下只有最小值不同,其余元素都相同。这样一来每个元素最多会搜索一次,所以最多是n次搜索,即O(n),

以下是Java的题解代码实现。

class Solution {
    public int findMin(int[] nums) {
        int N = nums.length;
        if (nums[0] < nums[N-1])
            return nums[0];
        
        return this.binarySearch(nums, 0, N-1, N);
    }
    
    private int binarySearch(int[] nums, int left, int right, int N) {
        if (right-left <= 1) {
            return Math.min(nums[left], nums[right]);
        }

        int mid = (left+right)/2;
        int leftNum = nums[mid-1];
        int rightNum = nums[mid+1];
        
        // 如果当前mid大于等于左邻,同时小于等于右邻
        if (nums[mid] >= leftNum && nums[mid] <= rightNum) {
            // 如果当前mid大于等于左端,同时大于右端,此时最小值在mid右边
            if (nums[mid] >= nums[0] && nums[mid] > nums[N-1]) {
                return this.binarySearch(nums, mid+1, right, N);
            }
            // 如果当前mid小于左端,同时小于等于右端,此时最小值在mid左边
            else if (nums[mid] < nums[0] && nums[mid] <= nums[N-1]) {
                return this.binarySearch(nums, left, mid-1, N);
            }
            // 如果当前mid同时等于左右两端,则两边都有可能
            else {
                return Math.min(this.binarySearch(nums, left, mid-1, N),
                                this.binarySearch(nums, mid+1, right, N));
            }
        }
        // 当前mid恰好是最小值(同时相等的情况在上面条件里已经考虑了,则不会进入当前条件分支)
        else if (nums[mid] <= leftNum && nums[mid] <= rightNum) {
            return nums[mid];
        }
        // 如果当前mid恰好大于右邻,同时大于等于左邻
        else {
            return rightNum;
        }
    }
}

以下是C++的题解代码实现。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int N = nums.size();
        if (nums[0] < nums[N-1])
            return nums[0];
        
        return this->_BinarySearch(nums, 0, N-1, N);
    }

private:
    int _BinarySearch(vector<int> &nums, int left, int right, int N) {
        if (right-left <= 1) {
            return min(nums[left], nums[right]);
        }
        
        int mid = (left+right)/2;
        // 如果当前mid大于等于左邻,同时小于等于右邻
        if (nums[mid] >= nums[mid-1] && nums[mid] <= nums[mid+1]) {
            // 如果当前mid大于等于左端,同时大于右端,此时最小值在mid右边
            if (nums[mid] >= nums[0] && nums[mid] > nums[N-1]) {
                return this->_BinarySearch(nums, mid+1, right, N);
            }
            // 如果当前mid小于左端,同时小于等于右端,此时最小值在mid左边
            else if (nums[mid] < nums[0] && nums[mid] <= nums[N-1]) {
                return this->_BinarySearch(nums, left, mid-1, N);
            }
            // 如果当前mid同时等于左右两端,则两边都有可能
            else {
                return min(this->_BinarySearch(nums, left, mid-1, N),
                           this->_BinarySearch(nums, mid+1, right, N));
            }
        }
        // 当前mid恰好是最小值(同时相等的情况在上面条件里已经考虑了,则不会进入当前条件分支)
        else if (nums[mid] <= nums[mid-1] && nums[mid] <= nums[mid+1]) {
            return nums[mid];
        }
        // 如果当前mid恰好大于右邻,同时大于等于左邻
        else {
            return nums[mid+1];
        }
    }
};

Search

    Table of Contents