刷题(0)-二分查找

目录

二分两种模板:

1)模板一(后半段的开头):

2)模板二(前半段的结尾):

3)查找值相同的二分:

查找值相同的二分小变种:找到第一个k和最后一个k的二分:


二分两种模板:

 ​​​​​​LeetCode暑期刷题打卡2019——Week1 二分专题_哔哩哔哩_bilibili

8:50 开始

95%二分查找的题目都满足两段性条件:

一段全都满足我们要找的条件,另一段全都不满足。

另外百分之5,不符合两段论的二分题,模板依旧是下面两个,但是check函数要设计好。

而二分法,有且仅有两种更新条件。

  •  r = mid, l = mid+1
    •   mid计算的时候,正常的向下取整即可  , 即
    •   mid = (l + r)/ 2
  •  l = mid, r = mid -1
    •    这种mid计算的时候,就需要向上取整, 即 
    •     mid = (l+r+1)/ 2

推导方法:
mid在左右区间怎么判断呢?
如果if (check(mid)) 条件成立,首先考虑一下答案是找最后一个满足要求的,还是第一个满足要求的:

如果是最后一个满足要求的 check(mid)成立就要,l = mid,  如果check(mid) 不成立则r = mid -1 ,

如果是第一个满足要求的check(mid)成立就要,r = mid,  如果check(mid) 不成立则l = mid +1 ,

为什么

  •  l = mid, r = mid -1
    •    这种mid计算的时候,就需要向上取整?, 即 
    •     mid = (l+r+1)/ 2
    •     因为考虑现在只有两个数的情况:
    •     ​ l = 0, r = 1, 假如mid向下取整则mid = 0, 如果此时更新方式又是l = mid = 0, 那 l 和r的值就没变,陷入死循环了。

记忆方法:

最后一个,更新方向是反的(左),  l = mid, mid更新也是反的,向上取整

第一个,更新方向是反的(右), r = mid, mid更新也是反的,向下取整

更新时,右比左小1

1)模板一(后半段的开头):

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + (r-l)/2 ;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;  // return left or right 均可,当然根据题意left也要检查一下
               // 因为数组里面可能就没有要找的东西
}

2)模板二(前半段的结尾):

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l +( r-l + 1)/2 ;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

应用:实现lower_bound (找到第一个大于等于val的)

因为是找第一个,所以用模板1

// 第一个大于等于val的
int lower_bound(vector<int> & vec, int val){
    
    int left=0,right=vec.size()-1;
    while(left<right){
        int mid =left+(right-left)/2;
        if(vec[mid]>=val)
            right=mid;
        else
            left=mid+1;
    }
    
    // 最后还是要检查一下的
    if(vec[left]>=val)
        return left;
    else
        return -1;
}

3)查找值相同的二分:

从一个有序数组中找到一个符合要求的精确值(如猜数游戏)。如查找值为Key的元素下标,不存在返回-1。

对于那些排序数组,找符合条件的解,一般考虑用二分查找
//二分查找  数组从小到大排列  时间复杂度 logN

找到数组arry中值等于key的下标 

int binary_search(int *arry, int len, int key) {
	int left = 0, mid, right = len - 1;
	while (left <= right) { // 有一个范围也得找,所以是《=
		
		mid = left + (right - left) / 2;     //不能写 (left+right)/2 数组比较长可能会溢出
		if (key < arry[mid]) right = mid - 1;
		else if (key>arry[mid]) left = mid + 1;
		else
			return mid;
	}
	return -1;
}

查找值相同的二分小变种:找到第一个k和最后一个k的二分:

可以不看这个,因为可以用上面的两个模板实现。

假如找第一个,那就是模板1,check函数是大于等于target

假如找最后一个,那就是模板2,check函数是小于等于target

当然最后别忘了,检查一下是否满足

int getfirst_k(int *arr,int len,int k){
	int left=0, right=len-1;
	int mid;
	while(left<=right){
		mid = left+((right-left)>>1);
		if(arr[mid]<k){
			left=mid+1;
		} else if(arr[mid]>k){
			right=mid-1;
		} else if(mid>0 && arr[mid-1]== k){
			right=mid-1;
		} else{
			return mid;
		}	
	}
	
	return -1;
}


int getlast_k(int *arr,int len ,int k){
	int left=0,right=len-1,mid;
	while(left<=right){
		mid=left+((right-left)>>1);
		if(arr[mid]<k){
			left=mid+1;
		} else if(arr[mid]>k){
			right=mid-1;
		} else if(mid<(len-1) && arr[mid+1]==k){
			left=mid+1;
		} else {
			return mid;
		}
	}
	
	return -1;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值