[LeetCode] 153. Find Minimum in Rotated Sorted Array 寻找旋转有序数组的最小值

 

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:

  • [4,5,6,7,0,1,2] if it was rotated 4 times.
  • [0,1,2,4,5,6,7] if it was rotated 7 times.

Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].

Given the sorted rotated array nums of unique elements, return the minimum element of this array.

You must write an algorithm that runs in O(log n) time.

 

Example 1:

Input: nums = [3,4,5,1,2]
Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.

Example 2:

Input: nums = [4,5,6,7,0,1,2]
Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.

Example 3:

Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times. 

 

Constraints:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • All the integers of nums are unique.
  • nums is sorted and rotated between 1 and n times.

 

这道题说是对一个有序数组进行了若干次旋转,让找出旋转数组的最小值,这里肯定不能通过直接遍历整个数组来寻找,过于简单粗暴,这样的话,旋不旋转就没有意义。但是可以利用旋转特点来进行优化,传统的找最小值是要遍历所有的数字的,这里由于旋转前是有序的,则数组中的第一个数字是最小的,发生旋转之后,第一个数字就不一定是最小的,但是如果从第一个数字往后遍历的话,应该是递增的,但如果遇到较小的数字,则一定是转折点,大家可以自行举例验证,这种方法在极端情况下还是线性的复杂度,但只要能过 OJ 的就是好方法,曾经代码如下

 

解法一:

class Solution {
public:
    int findMin(vector<int>& nums) {
        if (nums[0] <= nums.back()) return nums[0];
        for (int num : nums) {
            if (num < nums[0]) return num;
        }
        return -1;
    }
};

 

我们可以将时间复杂度从 O(n) 缩小到 O(lgn),这时候二分查找法就浮现在脑海。这里的二分法属于博主之前的总结帖 LeetCode Binary Search Summary 二分搜索法小结 中的第五类,也是比较难的那一类,没有固定的 target 值比较,而是要跟数组中某个特定位置上的数字比较,决定接下来去哪一边继续搜索。这里用中间的值 nums[mid] 和右边界值 nums[right] 进行比较,若数组没有旋转或者旋转点在左半段的时候,中间值是一定小于右边界值的,所以要去左半边继续搜索,反之则去右半段查找,最终返回 nums[right] 即可,参见代码如下:

 

解法二:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0, right = (int)nums.size() - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[right]) left = mid + 1;
            else right = mid;
        }
        return nums[right];
    }
};

 

下面这种分治法 Divide and Conquer 的解法,由热心网友 howard144 提供,这里每次将区间 [start, end] 从中间 mid 位置分为两段,分别调用递归函数,并比较返回值,每次取返回值较小的那个即可,参见代码如下:

 

解法三:

class Solution {
public:
    int findMin(vector<int>& nums) {
        return helper(nums, 0, (int)nums.size() - 1);
    }
    int helper(vector<int>& nums, int start, int end) {
        if (nums[start] <= nums[end]) return nums[start];
        int mid = (start + end) / 2;
        return min(helper(nums, start, mid), helper(nums, mid + 1, end));
    }
};

 

讨论:对于数组中有重复数字的情况,请参见博主的另一篇博文 Find Minimum in Rotated Sorted Array II

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/153

 

类似题目:

Search in Rotated Sorted Array

Find Minimum in Rotated Sorted Array II

 

参考资料:

https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/

https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/discuss/48493/Compact-and-clean-C%2B%2B-solution

https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/discuss/48484/A-concise-solution-with-proof-in-the-comment

 

LeetCode All in One 题目讲解汇总(持续更新中...)

posted @ 2014-10-18 13:05  Grandyang  阅读(16486)  评论(14编辑  收藏  举报
Fork me on GitHub