Leetcode中有很多衍变的二分查找算法问题。本质上都是讲问题逐步分解成更小的问题,然后求得问题的解。
题目一:Pow(x, n)
Implement pow(x, n)
思路:pow(x, n)操作就是将x与自己相乘n变,如果用重复n次循环,则可能会TLE, 比如[0.0001, 213783647]。那么如何加速这个过程呢?这里需要利用递归算法。
class Solution {
public:
double pow(double x, int n) {
if(n==0) return 1;
if(n==1) return x;
int exp = n<0?-n:n; //先把符号统一成正好
double result= exp%2==0?pow(x*x,exp/2):pow(x*x,exp/2)*x;
return n<0?1/result:result;
}
};
说明:像这样的算术运算都要主要符号问题。此外还有溢出问题。
题目二:Sqrt(x)
Implement int sqrt(int x). Compute and return the square root of x.
思路:这道题参照着网上流传的binary search模板写的,start=-1, end=n+1, 模板挺有意思的,没有==跳出的情况,唯一结束的条件是end-start>1不成立。当然最重要的是,还是啥不变式,这里是是 start<=sqrt<end !
class Solution {
public:
int sqrt(int x) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
long long int start = -1;
long long int end = (long long int)x+1;
while (end - start > 1) {
long long int mid = (end - start) / 2 + start;
if (mid*mid > x) {
end = mid;
} else {
start = mid;
}
}
return start;
}
};
Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. For example, [1,3,5,6], 5->2 [1,3,5,6], 2->1
思路:最直接的方法就是从左到右的遍历,遇到了就返回index,否则返回要插入的问题。但是由于这里是有序的,可以使用更有效的二分搜索算法。仿照上面得算法得到
class Solution {
public:
int searchInsert(int A[], int n, int target) {
if(n==0) return 0;
int start=-1, end=n;
while(end-start>1){
int mid=(end+start)/2;
if(target<A[mid]) end=mid;
else start=mid;
}
if(start>=0&&A[start]==target) return start;
return start+1;
}
};
题目三:Search in Rotated Sorted Array
Suppose a sorted array is rotated at some pivot unknown to you beforehand, i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2, You are given a target value to search. If found in the array return its index, otherwise return -1. You may assume no duplicate exists in the array.
思路:虽然没有完全有序,但是还是有规律的。这里的二分查找略需要改造下。在查找前需要判定目前在那一段中,保证有序区中查找。
class Solution {
public:
int search(int A[], int n, int target) {
int low=0;
int high=n-1;
while(low<=high){ //是有挪动的情况
int mid=(low+high)/2;
if(A[mid]==target) return mid;
if(A[low]<=A[mid]){ //前半部分是升序的
if(A[mid]<target) low=mid+1;
else if(target>=A[low]) high=mid-1;
else low=mid+1;
} //后半部分是升序的
else if(A[mid]>target) high=mid-1;
else if(A[high]>=target) low=mid+1;
else high=mid-1;
}
return -1;
}
};
题目四:Search in Rotated Sorted Array II
Follow up for "Search in Rotated Sorted Array": What if duplicates are allowed? Would this affect the run-time complexity? How and why?
思路:得额外增加一个判断重复的代码
class Solution {
public:
bool search(int A[], int n, int target) {
if(n==0 || A==NULL) return false;
return doSearch(A, target, 0, n-1);
}
bool doSearch(int A[], int target, int low, int high){
while(low<=high){
int mid=low+(high-low)/2;
if(A[mid]==target) return true;
//what abount duplicates
if(A[low]==A[mid]&&A[mid]==A[high]){ //先判断重复
for(int i=low; i<=high; i++)
if(A[i]==target) return true;
return false;
}
//
if(A[low]<=A[mid]){
if(A[mid]<target) low=mid+1;
else if(A[low]<=target) high=mid-1;
else low=mid+1;
}
else{
if(target<A[mid]) high=mid-1;
else if(target<=A[high]) low=mid+1;
else high=mid-1;
}
}
return false;
}
};
Given a sorted array of integers, find the starting and ending position of a given target value. Your algorithm's runtime complexity must be in the order of O(logn). If the target is not found in the array, return [-1, -1]. For example, given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4]
思路:还是和上面的题目类似,最直接的方法就是暴力查找。
vector<int> searchRange(int A[], int n, int target) {
int start, end;
start=end=-1;
vector<int> res;
for(int i=0;i<n;i++){
if(A[i]==target){
start=i;
while(i<n&&A[i]==target) i++;
end=i-1;
break;
}
}
res.push_back(start);
res.push_back(end);
return res;
}
改进的方法则是通过两次 二分查找方法分别找到range的start和end
vector<int> searchRange(int A[], int n, int target) {
vector<int> range(2, -1);
int lower = 0;
int upper = n;
int mid;
// Search for lower bound
while (lower < upper) { //这里只能是小于号,不能是<=, 否则出现死循环
mid = (lower + upper) / 2;
if (A[mid] < target)
lower = mid + 1;
else
upper = mid;
}
// If the target is not found, return (-1, -1)
if (A[lower] != target)
return range;
range[0] = lower;
// Search for upper bound
upper = n;
while (lower < upper) {
mid = (lower + upper) / 2;
if (A[mid] > target)
upper = mid;
else
lower = mid + 1;
}
range[1] = upper - 1;
return range;
}