二分查找法是比较常见的一种算法,下面我列举的题目都是常见的二分算法题目。
文章目录
剑指 Offer II 072. 求平方根
给定一个非负整数 x ,计算并返回 x 的平方根,即实现 int sqrt(int x) 函数。
正数的平方根有两个,只输出其中的正数平方根。
如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去。
class Solution {
//二分法
public int mySqrt(int x) {
int left=0;
int right=x;
int ans=-1;
while(left<=right){
int middle=(left+right)/2;
if((long)middle*middle<=x){
ans=middle;
left=middle+1;//看看有没有更接近的值
}else{
right=middle-1;
}
}
return ans;
}
}
还有一种方法使用牛顿迭代法:
有一个函数f(x),想求它的零点,但是求不出来,这时候就可以用牛顿迭代法。
- 先设想一个猜想值a,假如它是零点(其实它不是)
- 求出在a点处f(x)的切线方程,此方程的x轴截距(记做b),它是比a更好的近似值。
- 求出b的值,进入下一轮计算,刚刚求出的b就是新的a,重复上面的计算
- 什么时候结束呢?LeetCode的官方题解中是这样写的:每一次迭代后,我们都会距离零点更进一步,所以当相邻两次迭代得到的交点非常接近时,我们就可以断定,此时的结果已经足够我们得到答案了。
推导过程:
这道题的思路
1.设出f(x)的表达式,因为题目中要求求出x的算术平方根,所以函数设为f(x)=x^2-C,此处的C就是题目中给出的x,函数f(x)与x轴的交点就是题目中要求的算术平方根。当不好求出这个算术平方根的时候就用牛顿迭代法求出最接近算术平方根的那个值。
2.将题目给的x作为我们猜想的第一个点,程序里设为x0。
3.求出在此点的切线方程,求出切线与x轴的交点即为新的x0(如果难理解可以设为x1,然后再让新求出的x1赋给x0)。
4.循环条件,我这里设置的是while(x0 > x / x0),其实我觉得这样设更好理解(参考的其他大神的题解)。
其实如果记住了推导公式就可以直接用的,下图是推导过程
代码如下:
class Solution {
public int mySqrt(int x) {
long x0 = x; //防止x的数很大
if(x == 0){
//下面的x0会出现在分母中,所以把0单写出来
return x;
}
// 套用分析得到的迭代公式,计算出新的x0
while(x0 > x / x0) {
x0 = (x0 + x / x0) / 2; //求最接近准确值的点
}
//当遇到第一个x0 * x0 <= x的值时,表示已经得到最接近的结果,将x0的整数部分返回即可
if(x0*x0<=x){
return (int)x0;
}
return -1;
}
}
还可进行递归实现
class Solution {
public int mySqrt(int x) {
if(x==0){
return x;
}
long x0=x;
return res(x0,x0);
}
public int res(long x0,long x){
int result=-1;
if(x0*x0<=x){
return (int)x0;
}
if(x0 > x / x0){
result= res(((x0 + x / x0) / 2),x);
}
return result;
}
}
leedcode 35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
代码如下:
class Solution {
public int searchInsert(int[] nums, int target) {
int left=0;
int right=nums.length-1;
int ans=nums.length;
while(left<=right){
int middle=(left+right)/2;
//使用二分法不断逼近大于等于target的值,这样插入到大于等于target的nums [middle]的middle位置即可
if(target<=nums [middle]){
ans=middle;
right=middle-1;
}else{
left=middle+1;
}
}
return ans;
}
}
leedcode 367. 有效的完全平方数
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要 使用任何内置的库函数,如 sqrt 。
示例 1:
输入:num = 16 输出:true
示例 2:
输入:num = 14 输出:false
代码如下:
class Solution {
public boolean isPerfectSquare(int num) {
int left=0;
int right=num;
while(left<=right){
int middle=left-(left-right)/2;
long square=(long)middle*middle;
if(square<num){
left=middle+1;
}else if(square>num){
right=middle-1;
}else{
return true;
}
}
return false;
}
}
leedcode 34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 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]
代码如下:
class Solution {
public int[] searchRange(int[] nums, int target) {
int firstIndex=searchFirst(nums,target);
int lastIndex=searchLast(nums,target);
return new int[]{firstIndex,lastIndex};
}
public int searchFirst(int[] nums,int target){
int left=0,right=nums.length-1;
while(left<=right){
int middle= left+(right-left)/2;
if(target>nums[middle]){
left=middle+1;
}else if(target<nums[middle]){
right=middle-1;
}else{
//如果mid位于首个位置或者他的前一个位置的元素和他不相等,说明此时mid位置对应的元素就是第一个
if(middle==0||nums[middle-1]!=nums[middle]){
return middle;
}else{
//如果不是说明,mid位置前面还有和他相同的元素,这时我们需要再次进行二分法进行逼近,也就是right=middle-1
right=middle-1;
}
}
}
return -1;
}
public int searchLast(int[] nums,int target){
int left=0,right=nums.length-1;
while(left<=right){
int middle=left+(right-left)/2;
if(target>nums[middle]){
left=middle+1;
}else if(target<nums[middle]){
right=middle-1;
}else{
//如果mid位于最后一个位置或者他的后一个位置的元素和他不相等,说明此时mid位置对应的元素就是最后一个
if(middle==nums.length-1||nums[middle]!=nums[middle+1]){
return middle;
}else{
//如果不是说明,mid位置后面还有和他相同的元素,这时我们需要再次进行二分法进行逼近,也就是eft=middle+1
left=middle+1;
}
}
}
return -1;
}
}