二分查找解决旋转数组
二分查找用到很多大于小于号, 每次都得判断是否需要加等于, 着实费劲..
题目很容易理解, 也可以快速想出思路:
因为数组旋转前是有序的, 所以旋转后会保持一定的有序性, 可以对有序部分进行二分查找或缩小查找查找范围.
举个例子:
如果原数组为: 2 5 6 7 0 1 . 数组最左端的值为2, 第一轮num[mid]值为6, 下标从0到 mid 所对应的值有序,
如果num[start]<=target<num[mid], 就去mid前半部分继续找, 否则去后半部分找.
如果原数组为: 2 5 6 0 1 2 . 数组最左端的值为2, 第一轮num[mid]值为0, 下标从 mid 到 last-1 所对应的值有序,
如果num[mid]<target<=num[last-1], 就去mid前半部分继续找, 否则去后半部分找.
如果是 1 1 1 1 0 1 1 . num[start] == num[mid],
那直接让 start+1, 去掉这个干扰项, 可以保证不影响旋转数组原来的有序性质.
代码1:
public:
bool search(vector<int>& nums, int target) {
int size = nums.size();
int first = 0;
int last = size;
while(first < last){
int mid = first + (last - first)/2;
if(nums[mid] == target){
return true;
}
if(nums[first] < nums[mid]){
if(nums[first]<= target &&
target < nums[mid])
last = mid;
else
first = mid+1;
}else if(nums[first] > nums[mid]){
if(nums[mid] < target &&
target <= nums[last-1] )
first = mid+1;
else
last = mid;
}
else if(nums[first] == nums[mid]){
first++;
}
}
return false;
}
这里的难点是判断哪里需要加"="号,
根据上述分析可以先写出代码结构
public:
bool search(vector<int>& nums, int target) {
// ... 省略
while(first < last){
int mid = first + (last - first)/2;
if(nums[mid] == target){
return true;
}
if(nums[first] < nums[mid]){
// ??? block1
}else if(nums[first] > nums[mid]){
// ??? block2
}
else if(nums[first] == nums[mid]){
first++;
}
}
return false;
}
首先可以先构想一个示例,
2 5 6 7 8 0 1
mid 第一次代表的值为7, 如果 target 落在了 2 和 7 之间, 那就可以把搜索区间缩小到 first 到 mid 部分, 否则就缩小到 mid+1 和 last 之间 (左开右闭原则)
所以 block1 部分的代码:
if(nums[first] <= target && target < nums[mid])
last = mid;
else
first = mid+1;
为什么 nums[first] <= target 要加等于号, 而 target < nums[mid]) 不加?
因为如果 target 落在 mid 的左半部分时, nums[first] 这个数字一定会参与到下一次的二分查找, 必须保证包含nums[first]这个数字, 所以nums[first] <= target 要加等于号.
而 target 是否等于 nums[mid] 这个条件已经在
if(nums[mid] == target)
{ return true; }
判断过了, 所以不用判断 target 是否等于 nums[mid] .
再构想一个示例,
2 5 0 1 1 2 2
mid 第一次代表的值为1, 如果 target 落在了 1 和 2 之间, 那就可以把搜索区间缩小到 mid+1 到 last 部分, 否则就缩小到 first 和 mid 之间.
所以 block2 部分的代码:
if(nums[mid] < target && target <= nums[last-1] )
first = mid+1;
else
last = mid;
为什么 target <= nums[last-1] 要加等于号, 而 nums[mid] < target 不加?
因为如果 target 落在 mid 的右半部分时, nums[last-1] 这个数字一定会参与到下一次的二分查找, 必须保证包含nums[last-1]这个数字, 所以nums[first] <= target 要加等于号.
target 是否等于 nums[mid] 这个条件如上所述, 已存在, 不用判断.
=======================
按理说到这里可以结束了, 已经写出了一份标准代码.
但是如果第一层思路稍有不同, 也可以写出一份效果完全一样的代码.
如果写出框架时不是比较 nums[first] 和 nums[mid] 的关系,
而是比较 nums[first] 和 nums[mid] 的关系, 写出如下框架:
if(nums[mid] < nums[last-1]){
// ???
}else if(nums[mid] > nums[last-1]){
// ???
}else if(nums[mid] == nums[last-1]){
// ???
}
判断思路与上同理, 可以自行试写.
完整代码如下:
public:
bool search(vector<int>& nums, int target) {
int size = nums.size();
int first = 0;
int last = size;
while(first < last){
int mid = first + (last-first) /2 ;
if(nums[mid] == target){
return true;
}
if(nums[mid] < nums[last-1]){
if(nums[mid] < target && target <= nums[last-1]){
first = mid + 1;
}else{
last = mid;
}
}else if(nums[mid] > nums[last-1]){
if(nums[first] <= target && target < nums[mid]){
last = mid;
}else{
first = mid + 1;
}
}else if(nums[mid] == nums[last-1]){
last--;
}
}
return false;
}