二分搜索-旋转数组
1. 给你一个旋转数组,和一个目标数,问你在这个数组中是否存在这个目标数?(数组无重复元素/有重复元素)
2. 给你一个旋转数组,要求旋转数组的最小值
解析:
1.
1)如果数组不存在重复元素
在二分搜索过程中,判断A[l]与A[mid]的关系
a. A[l] < A[mid] ,可能是下图两种情况:
或者
第一种情况是数组本身单调递增,第二种情况是左半段单调递增,因此,只需要判断target是否落在这个区间以内
b. A[l] == A[mid]
由于在二分搜索过程中,会先判断target是否等于A[mid],因此在该中情况下,target != A[l],因此l = mid + 1
c. A[l] > A[mid],只可能是一下一种情况:

即右半段是单调递增的,因此只需要判断target是否落在右边段区间内
具体代码如下:
class Solution { public: int search(vector<int> &nums, int target) { int n = nums.size(); int ans = -1; if(n <= 0) { return ans; } int low = 0; int high = n - 1; while(low <= high) { int mid = (low + high) >> 1; if(target == nums[mid]) { ans = mid; break; } if(nums[low] < nums[mid]) { if(target >= nums[low] && target < nums[mid]) { high = mid - 1; } else { low = mid + 1; } } else if(nums[low] == nums[mid]) { low = mid + 1; } else { if(target > nums[mid] && target <= nums[high]) { low = mid + 1; } else { high = mid - 1; } } } return ans; } };
2)如果数组存在重复元素
上述a和c的情况基本是一直的,但是对于b情况,当A[l]==A[mid]时,无法判断是左半段单调递增还是右边半段单调递增,因为存在以下两种可能的情况:
i. 1 0 1 1 1 和1 1 1 0 1,唯一可以判断的是target != A[l],因此,此时只需要++l即可,具体代码如下:
class Solution { public: bool search(vector<int>& nums, int target) { bool found = false; int n = nums.size(); if(n <= 0) { return found; } int l = 0; int r = n - 1; while(l <= r) { int m = (l + r) >> 1; if(nums[m] == target) { found = true; break; } if(nums[l] < nums[m]) { if(target >= nums[l] && target < nums[m]) { r = m - 1; } else { l = m + 1; } } else if(nums[l] > nums[m]) { if(target > nums[m] && target <= nums[r]) { l = m + 1; } else { r = m - 1; } } else { ++l; } } return found; } };
2. 和1是一样的题目,只不过要求数组的最小值
1)如果数组不存在重复元素
一共有四种情况:
a. A[l] < A[r]
说明整个数组是单调递增的,因此最小值为A[l],直接返回即可
b. A[l] < A[mid]
说明左半段单调递增,如下图情况:

说明最小值一定在mid的右边,因此更新l = mid + 1
c. A[l] == A[mid]
因为没有重复的元素,如果整个数组只有一个元素,l = mid + 1 之后(l <=r 循环判断会失败)则返回A[r];否则有多个元素,在右半段一定可以找到一个<= A[l]的,因此也是l = mid + 1
d. A[l] > A[mid]
说明右半段单调递增,如下图情况:

因此最小值在mid的左边或者在mid上,因此r = mid
全部代码如下(注意最后返回A[r],这个可以用具体的例子试以下)
class Solution { public: int findMin(vector<int>& nums) { int ans = -1; int n = nums.size(); if(n <= 0) { return ans; } int l = 0; int r = n - 1; while(l <= r) { if(nums[l] < nums[r]) { ans = nums[l]; return ans; } int m = l + ((r - l) >> 1); if(nums[l] <= nums[m]) { l = m + 1; } else { r = m; } } return nums[r]; } };
2)如果数组存在重复元素
如果存在重复的元素,对于上述等于的情况,实际上是无法判断最小值在左边区间,还是在右边区间,但无论在左半段或者右半段,最小值一定在l的右边或等于l,因此++l总是可以找到最小值,具体代码如下:
class Solution { public: int findMin(vector<int>& nums) { int ans = -1; int n = nums.size(); if(n <= 0) { return ans; } int l = 0; int r = n - 1; while(l <= r) { if(nums[l] < nums[r]) { ans = nums[l]; return ans; } int m = (l + r) >> 1; if(nums[l] < nums[m]) { l = m + 1; } else if(nums[l] > nums[m]) { r = m; } else { ++l; } } return nums[r]; } };

浙公网安备 33010602011771号