注:以下代码均为c++
1. x的平方根
分析后应使用模板2。
int mySqrt(int x){
// 1. 确定二分边界
int l = 0, r = x;
// 2. 编写二分框架,确定二分条件和区间更新方法
while(l < r){
//int mid = (l + r + 1)/2; //注意:这一步为了防止溢出将int变为longlong。也可以将除法写成位运算符,简化计算。
int mid = ((long long)l + r + 1) >> 1;
//if(mid * mid <= x) l = mid; //注意:这里为了防止溢出,将*转换到不等式右面变成/
if(mid <= x/mid) l = mid;
else r = mid - 1;
}
return r; //这里返回r和l都行,因为此时r==l
}
注意:防止溢出的策略。
2. 搜索插入位置
int searchInsert(vector<int>& nums, int target) {
int n=nums.size();
int l =0, r=n-1;
//处理边界
if(target>nums[r])
return n;
while(l<r){
int m =(l+r)/2;
if(nums[m]>=target) r=m;
else l=m+1;
}
return l;
}
3. 在排序数组中查找元素的第一个和最后一个位置
vector<int> searchRange(vector<int>& nums, int target) {
int n = nums.size();
if(n ==0) return {-1, -1};
int l = 0, r = n-1;
while(l < r){
int m = (l + r) >> 1;
if(nums[m] >= target) r = m;
else l = m + 1;
}
if(nums[l] != target) return {-1, -1};
int start = l;
l = 0, r = n - 1;
while(l < r){
int m = (l + r + 1) >> 1;
if(nums[m] <= target) l = m;
else r = m - 1;
}
int end = l;
return {start, end};
}
4. 搜索二维矩阵
- 我自己写的:分两次二分(我在模仿上一题hh),但是可以将二维数组展开成一维,只要找好行和列的对应即可。
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int l = 0, r = m - 1;
while(l < r){
int m = (l + r + 1) >> 1;
if(matrix[m][0] <= target) l = m;
else r = m -1 ;
}
int row = l;
l = 0, r = n -1;
while(l < r){
int m = (l + r + 1) >> 1;
if(matrix[row][m]<= target) l = m;
else r = m - 1;
}
int col = l;
if(matrix[row][col] == target) return true;
else return false;
}
- 答案:一次二分:将二维数组展成一维。
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int l = 0, r = m * n -1;
while(l < r){
int mid = (l + r) >> 1;
if(matrix[mid/n][mid%n] >= target) r = mid;
else l = mid + 1;
}
if(matrix[l/n][l%n] == target) return true;
else return false;
}
5. 寻找旋转排列数组中的最小值
int findMin(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n-1;
while(l < r){
int m = (l + r) >> 1;
if(nums[m] > nums[r]) l = m + 1;
else r = m;
}
return nums[l];
}
6. 搜索旋转排序数组
int search(vector<int>& nums, int target) {
//先找临界值
int n = nums.size();
int l = 0, r = n - 1;
while(l < r){
int m = (l + r) >> 1;
if(nums[m] > nums[r]) l = m + 1;
else r = m;
}
//再在两段上分别二分
if(target <= nums[n-1]) r = n - 1;
else l = 0, r = r - 1;
while(l < r){
int m = (l + r) >> 1;
if(nums[m] < target) l = m + 1;
else r = m;
}
if(target == nums[l]) return l;
return -1;
}
7. 第一个错误的版本
int firstBadVersion(int n) {
int l = 1, r = n;
while(l < r){
int m = (long long)l + r >> 1; //注意防止溢出:转long long
if(isBadVersion(m)) r = m;
else l = m + 1;
}
return l;
}
8. 寻找峰值
这道题就是那5%,没有两段性,仍然可以使用二分查找。
int findPeakElement(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while(l < r){
int m = l + r >> 1;
if(nums[m] > nums[m+1]) r = m;
else l = m + 1;
}
return l;
}
9. 寻找重复数
没有两段性的二分。
int findDuplicate(vector<int>& nums) {
int n = nums.size();
int l = 1, r = n - 1;
while(l < r){
int m = l + r >> 1;
int cnt = 0; //统计左侧区间中抽屉里的苹果数
for(auto x: nums){
if(x >= l && x <= m) cnt++;
}
if(cnt > m-l+1) r = m; //如果左侧苹果的数量比抽屉的数量大,则到左侧继续寻找。
else l = m + 1;
}
return l;
}
10. H指数2
有两段性。
注意这里h的范围是0 ~ n,而数组的范围是0 ~ n-1,所以在计算倒数第h个数时用的是n-1-h+1
int hIndex(vector<int>& citations) {
int n = citations.size();
int l = 0, r = n;
while(l < r){
int m = (l + r + 1) >> 1;
if(citations[n-m] >= m) l = m;
else r = m - 1;
}
return l;
}