[LeetCode] 154. Find Minimum in Rotated Sorted Array II

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

  • [4,5,6,7,0,1,4] if it was rotated 4 times.
  • [0,1,4,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 that may contain duplicates, return the minimum element of this array.

Example 1:

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

Example 2:

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

Constraints:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums is sorted and rotated between 1 and n times.

Follow up: This is the same as Find Minimum in Rotated Sorted Array but with duplicates. Would allow duplicates affect the run-time complexity? How and why?

Note:

寻找旋转有序数组的最小值之二。

思路跟版本一很接近,唯一多的一个条件是如果input里面有重复数字怎么办。这里需要多一个判断条件,如果判断不出来mid和end之间的大小关系,只能尝试end--,因为说明end和mid的值是一样的。所以这题worst case的时间会到O(n)。这道题跟153题一样,我还是选择使用 while (start + 1 < end) 这个模板,这样我最后需要额外判断 nums[start] 和nums[end],但是因为是找最小值而不是找目标值所以这个模板我个人觉得做起来比较方便。

时间O(logn), worst case O(n)

空间O(1)

JavaScript实现

 1 /**
 2  * @param {number[]} nums
 3  * @return {number}
 4  */
 5 var findMin = function(nums) {
 6     // corner case
 7     if (nums === null || nums.length === 0) return -1;
 8 
 9     // normal case
10     let start = 0;
11     let end = nums.length - 1;
12     while (start + 1 < end) {
13         let mid = Math.floor(start + (end - start) / 2);
14         if (nums[mid] < nums[end]) {
15             end = mid;
16         } else if (nums[mid] > nums[end]) {
17             start = mid + 1;
18         } else {
19             end--;
20         }
21     }
22     if (nums[start] < nums[end]) return nums[start];
23     else return nums[end];
24 };

 

Java实现

 1 class Solution {
 2     public int findMin(int[] nums) {
 3         int start = 0;
 4         int end = nums.length - 1;
 5         // start + 1 < end
 6         // 举例,start - 0, end = 3
 7         // 中间隔了起码有start + 1和start + 2两个下标
 8         // 这样跳出while循环的时候,end = start + 1
 9         // 才有了最后的两个特判
10         while (start + 1 < end) {
11             int mid = start + (end - start) / 2;
12             if (nums[mid] < nums[end]) {
13                 end = mid;
14             } else if (nums[mid] > nums[end]) {
15                 start = mid;
16             } else {
17                 end--;
18             }
19         }
20         if (nums[start] < nums[end]) {
21             return nums[start];
22         } else {
23             return nums[end];
24         }
25     }
26 }

 

这里我再补充另一种二分的写法,是左闭右闭区间,但是中间没有等号,如果理解不透彻容易写成死循环。注意这个 while 跳出的条件是 start == end,为什么不能加等号是因为 start 和 end 都在下标范围内。当我们找到位于 mid 的元素时,如果他比 end 元素要大,说明最小值一定在右半段,且 nums[mid] 一定不是最小值,所以 start = mid + 1;但是 else 的时候,end 就只能等于 mid,因为最小值有可能在 mid 位置上。

这个写法对应了 153 题的第二种写法,唯一多的一步是处理重复元素。

Java实现

 1 class Solution {
 2     public int findMin(int[] nums) {
 3         int start = 0;
 4         int end = nums.length - 1;
 5         while (start < end) {
 6             int mid = start + (end - start) / 2;
 7             if (nums[mid] > nums[end]) {
 8                 start = mid + 1;
 9             } else if (nums[mid] < nums[end]) {
10                 end = mid;
11             } else {
12                 end--;
13             }
14         }
15         return nums[start];
16     }
17 }

 

LeetCode 题目总结

posted @ 2019-11-04 13:39  CNoodle  阅读(355)  评论(0编辑  收藏  举报