题目来源
题目描述
题目解析
思路
- 旋转排序数组nums可以被拆分成2个排序数组nums1,nums2
- 左边是一个递增数组
- 右边也是一个递增数组
- 左右两部分相交的位置出现了一个异常点,小的数字在大的数字后面
那么,只要我们可以找到异常点,也就找到了旋转数组的最小元素。
- 从下面我们也可以看出 nums1任一元素 >= nums2任一元素
- 因此,考虑二分法寻找此两数组的分界点nums[i](即第二个数组的首个元素)
方法
-
设置 left, right指针在 nums数组两端,mid为每次二分的中点:
- 如果 nums[mid] > nums[right]时,mid一定在第一个排序数组中,i一定满足mid < i <= right,因此令left = mid + 1
- 如果nums[mid] < nums[right]时,mid 一定在第 2 个排序数组中,i 一定满足 left < i <= mid,因此执行 right = mid;
- 如果nums[mid] == nums[right]时,是此题对比 153题 的难点(原因是此题中数组的元素可重复,难以判断分界点 i 指针区间);
- 例如 [1, 0, 1, 1, 1]和 [1, 1, 1, 0, 1],在 left = 0, right = 4, mid = 2 时,无法判断 midmid 在哪个排序数组中。
- 我们采用 right = right - 1 解决此问题:
class Solution {
public:
int findMin(vector<int>& nums) {
int L = 0, R = nums.size() - 1;
while (L < R){
int M = (L + R) / 2;
if(nums[M] > nums[L]){
L = M + 1;
}else if(nums[M] < nums[L]){
R = M;
}else{
R = R - 1;
}
}
return nums[L];
}
};
如果想不到 right = right - 1 怎么办?我们可以从头到尾遍历一下剩下的区间,找到那个最小的元素。
class Solution {
public int minArray(int[] numbers) {
// 设置 left, right 指针分别指向 numbers 数组左右两端
// left 指向当前区间的最左边位置,所以初始化为 0
int left = 0;
// right 指向当前区间的最右边位置,所以初始化为 nums.length - 1
int right = numbers.length - 1;
// 循环进行二分查找,直到左端点位置超过了右端点
// 或者在循环过程中找到了起始位置
while (left < right) {
// mid 为中点(这里向下取整,比如 ( 2 + 7 )/ 2 = 4 )
int mid = (left + right) / 2;
// 当 mid 点所在元素大于数组末端的元素时,由于原来的数组是递增有序的,此时出现了异常,大的数在前面
// 所以旋转点在 [ mid + 1, end ] 区间里面
if (numbers[mid] > numbers[right]){
// 所以旋转点在 [ mid + 1, end ] 区间里面 ,更新 left 的位置为 mid + 1
left = mid + 1;
// 当 mid 点所在元素小于数组末端的元素时,由于原来的数组是递增有序的
// 所以旋转点在 [ left, mid ] 区间里面
}else if (numbers[mid] < numbers[right]){
// 旋转点在 [ left, mid ] 区间里面 ,更新 right 的位置为 mid
right = mid;
// 此时,出现了 numbers[mid] = numbers[end] 的情况,无法判断
// [ start , mid ] 为有序数组区间
// 还是 [ mid , end ] 为有序数组区间
// 比如: [1, 0, 1, 1, 1] 和 [1, 1, 1, 0, 1]
}else{
// 所以这里采取遍历的方式
return findMin(numbers,left,right);
}
}
return numbers[left];
}
// 从头到尾遍历 numbers ,获取到最小值
public int findMin(int[] numbers,int left,int right){
// 默认为数组的第一个元素为最小值
int result = numbers[left];
// 从头到尾遍历 numbers
for(int i = left;i <= right;i++){
// 当发现此时遍历的元素值小于 result
if (numbers[i] < result) {
// 那么更新 result
result = numbers[i];
}
}
// 返回 numbers 中的最小值
return result;
}
}