介绍
二分的本质就是找边界的,至于什么二分查找,二分答案什么的,名字花里胡哨的,但他们的实质都是找边界,所以没有必要分要把他们分开,也不必把他们理解的多么高深。
二分模板
以下模板是他人总结的,该模板避免了众多细节。关于l + r >> 1与l + r + 1 >> 1的区别是在出现mid - 1后,如果出现此则使用l + r + 1 >> 1,以免造成边界问题。
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
题目
1.704. 二分查找 - 力扣(LeetCode) (难度:简单)
描述:
- 给定一个
n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。
示例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
代码:
int search(int* nums, int numsSize, int target) {
int l = 0,r = numsSize-1;
while(l<r)
{
int mid = (l+r+1)/2;
if(nums[mid]<= target) l = mid;
else r = mid-1;
}
if(nums[l]!=target) return -1;
else return l;
}
2.35. 搜索插入位置 - 力扣(LeetCode) (难度:简单)
描述:
- 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。(请必须使用时间复杂度为
O(log n)的算法。)
示例:
输入: nums = [1,3,5,6], target = 5
输出: 2
代码:
int searchInsert(int* nums, int numsSize, int target) {
int l = 0, r = numsSize - 1;
while(l < r)
{
int mid = l + r >> 1;
if(nums[mid] >= target ) r = mid;
else l = mid + 1;
}
if(nums[l] < target) return l+1;
else return l;
}
3.34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode) (难度:中等)
描述:
- 给你一个按照非递减顺序排列的整数数组
nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1, -1]。你必须设计并实现时间复杂度为O(log n)的算法解决此问题。
示例:
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
示例 3:
输入: nums = [], target = 0
输出: [-1,-1]
代码:
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
int* ans = (int*)malloc(sizeof(int)*2);
*returnSize = 2;
if(numsSize == 0)
{
ans[0] = -1;
ans[1] = -1;
return ans;
}
int l1 = 0, r1 = numsSize-1;
while(l1 < r1 )
{
int mid = l1 + r1 >> 1;
if(nums[mid] >= target) r1 = mid;
else l1 = mid + 1;
}
if(nums[l1]== target) ans[0] = l1;
else ans[0] = -1;
int l2 = 0, r2 = numsSize - 1;
while(l2 < r2)
{
int mid = l2 + r2 + 1 >> 1;
if(nums[mid] <= target) l2 = mid;
else r2 = mid - 1;
}
if(nums[l2] == target) ans[1] = l2;
else ans[1] = -1;
return ans;
}
4.69. x 的平方根 - 力扣(LeetCode)(难度:简单)
描述:
- 给你一个非负整数
x,计算并返回x的 算术平方根 。 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 注意: 不允许使用任何内置指数函数和算符,例如pow(x, 0.5)或者x ** 0.5。 示例:
示例 1:
输入: x = 4
输出: 2
示例 2:
输入: x = 8
输出: 2
解释: 8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
代码:
int mySqrt(int x) {
long long l = 0, r = x;
while(l < r)
{
long long mid = (l + r + 1 ) >> 1;
if(mid * mid <= x ) l = mid;
else r = mid - 1;
}
return (int)l;
}
注意:
1.int不可以,要用long long。
5.367. 有效的完全平方数 - 力扣(LeetCode)(难度:简单)
描述:
- 给你一个正整数
num。如果num是一个完全平方数,则返回true,否则返回false。完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数,如sqrt。
示例:
示例 1:
输入: num = 16
输出: true
解释: 返回 true ,因为 4 * 4 = 16 且 4 是一个整数。
示例 2:
输入: num = 14
输出: false
解释: 返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。
代码:
bool isPerfectSquare(int num) {
long long l = 0, r = num;
while(l < r)
{
long long mid = (l + r + 1) >> 1;
if(mid * mid <= num) l = mid;
else r = mid - 1;
}
if( l * l == num) return true;
else return false;
}