[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 rotated4
times.[0,1,2,4,5,6,7]
if it was rotated7
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 between1
andn
times.
寻找旋转有序数组的最小值。
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路还是用二分法做。这道题我选择使用 while (start + 1 < end) 这个模板,这样我最后需要额外判断 nums[start] 和nums[end],但是因为是找最小值而不是找目标值所以这个模板我个人觉得做起来比较方便。
因为数组是旋转过的所以数组的最大值很有可能在中间,所以需要这样判断
- 如果mid小于end,说明mid - end是一段递增,直接就end = mid了,因为最小值一定在mid左侧
- 如果mid大于end,说明start - mid是一段递增,最小值在mid右侧,start = mid
时间O(logn)
空间O(1)
JavaScript实现
1 /** 2 * @param {number[]} nums 3 * @return {number} 4 */ 5 var findMin = function(nums) { 6 let start = 0; 7 let end = nums.length - 1; 8 while (start + 1 < end) { 9 let mid = Math.floor(start + (end - start) / 2); 10 if (nums[mid] < nums[end]) { 11 end = mid; 12 } else { 13 start = mid; 14 } 15 } 16 if (nums[start] < nums[end]) return nums[start]; 17 else return nums[end]; 18 };
这里稍微解释一下while循环里面的if条件判断。第十行,如果nums[mid] < nums[end],就说明后半段一定是升序的,没有被rotate过的,所以end = mid;同理可以得到如果nums[mid] >= nums[end],那么没有被rotate过的部分一定在前半段,所以start = mid。跳出while循环的时候说明没找到,但是此刻start和end满足如下关系,left < target < right, left + 1 = right,所以有最后的判断。
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 { 15 start = mid; 16 } 17 } 18 if (nums[start] < nums[end]) { 19 return nums[start]; 20 } else { 21 return nums[end]; 22 } 23 } 24 }
这里我再补充另一种二分的写法,是左闭右闭区间,但是中间没有等号,如果理解不透彻容易写成死循环。注意这个 while 跳出的条件是 start == end,为什么不能加等号是因为 start 和 end 都在下标范围内。当我们找到位于 mid 的元素时,如果他比 end 元素要大,说明最小值一定在右半段,且 nums[mid] 一定不是最小值,所以 start = mid + 1;但是 else 的时候,end 就只能等于 mid,因为最小值有可能在 mid 位置上。
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 { 10 end = mid; 11 } 12 } 13 return nums[start]; 14 } 15 }