二分查找 【模板+中间值问题】

😃前言

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。它要求元素具有有序性,或者具有跟有序性类似的性质,能够不断地缩小查找范围。

查找操作主要有一下几点:

1、确定需要查找的值,设立基准值(一般是中间值),设立判断条件,判断基准值是否满足某种性质

2、以基准值为边界,划分左右两个区间

3、根据判断条件是否成立,分析结果可能存在的区间,更新查找范围

4、重复以上操作,直到区间不存在(左边界大于右边界)或查找到需要的值时,查找结束


😕二分查找动图演示

在这里插入图片描述


😴代码模板

我们这里以整数的有序序列为例,演示二分查找的代码和需要注意的事项。整数二分的代码分为两种情况:

  1. 判断条件成立时新区间需要更新在左半区间,即[l, mid]
  1. 判断条件成立时新区间需要更新在右半区间,即[mid, r]

需要注意的是不管哪种情况,条件成立都需要将取到mid,因为mid可能为答案

bool check(int x) {/* ... */} // 检查答案是否满足某种性质

// 查找左区间
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:条件成立,新区间在[l, mid]
int SearchLeft(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]时使用:条件成立,新区间在[mid, r]
int SearchRight(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

❗️ 使用哪个模板问题 ❗️

决定使用哪个模板,是根据check(mid)来决定的:

check(mid)就是判断答案在左半区间还是右半区间

1、如果check(mid)是判断答案在左半区间,使用SearchLeft模板(mid不需要+1):

如果在条件成立,说明答案在左半区间,也就是[l, mid],新区间为[l, mid],区间更新方式为r = mid
如果条件不成立,说明答案在右半区间,也就是[mid + 1, r],新区间为[mid + 1, r],区间更新方式为l = mid + 1

2、如果check(mid)是判断答案在右半区间,使用第二个模板(mid需要+1):

如果在条件成立,说明答案在右半区间,也就是[mid, r],新区间为[mid, r],更新条件为l = mid
如果条件不成立,说明答案在左半区间,也就是[l, mid - 1],新区间为[l, mid - 1],更新条件为r = mid - 1


💢 mid为何+1问题 💢

在这两种情况中,区间划分好理解,不就是一左一右、一加一减嘛。但是在mid为何需要加1可能会让很多人疑惑,我在学习的时候也是迷糊了好久。现在将从大佬那里总结到的经验分享一下:

1、在SearchLeftmid 为何不需要 +1

设左右两个边界只相差1,即l = r - 1时,如果 mid + 1mid 向下取整 ,即mid == r ,如果条件刚好成立的话,就会导致更新完区间后r还是等于mid,等于没有更新,这时候就是一个死循环了。所以mid 不需要 +1,进行向上取整,让mid == l,破坏死循环的条件。

2、在SearchRightmid为何需要 +1

设左右两个边界只相差1,即l = r - 1时,当mid不 +1 时mid是向上取整的,所以mid == l,如果条件刚好成立的话,就会导致更新完 区间后l还是等于mid,等于没有更新,这时候就是一个死循环了。所以需要mid+1进行向下取整,让mid == r,破坏死循环的条件。

大佬原话:

对于mid + 1与否我觉得是为了让区间平分
mid = left + right >> 1; 这里mid是上中位数
mid = left + right + 1 >> 1; 这里mid是下中位数
如果取left = mid, 即[mid, right], 则mid取下中位数才能平分区间
如果取right = mid, 即[left, mid], 则mid取上中位数才能平分区间


完结散花🌈🌈🌈
在这里插入图片描述

  • 47
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 39
    评论
1. 二分法 5 1.1. 什么是二分查找 5 1.2. 如何识别二分法 5 1.3. 二分法模板 6 1.3.1. 模板一 6 1.3.1.1. 模板代码 6 1.3.1.2. 关键属性 7 1.3.1.3. 语法说明 7 1.3.1.4. Lc69:x的平方根 8 1.3.1.5. Lc374:猜数大小 9 1.3.1.6. Lc33:搜索旋转数组 11 1.3.2. 模板二 13 1.3.2.1. 模板代码 13 1.3.2.2. 关键属性 14 1.3.2.3. 语法说明 14 1.3.2.4. Lc278:第一个错误版本 14 1.3.2.5. Lc162:寻找峰 16 1.3.2.6. Lc153:寻找旋转排序数组最小 19 1.3.2.7. Lc154:寻找旋转排序数组最小II 20 1.3.3. 模板三 22 1.3.3.1. 模板代码 22 1.3.3.2. 关键属性 23 1.3.3.3. 语法说明 23 1.3.3.4. LC-34:在排序数组中查找元素的第一个和最后一个 23 1.3.3.5. LC-658:找到K个最接近的元素 25 1.3.4. 小结 28 1.4. LeetCode中二分查找题目 29 2. 双指针 30 2.1. 快慢指针 31 2.1.1. 什么是快慢指针 31 2.1.2. 快慢指针模板 31 2.1.3. 快慢指针相关题目 32 2.1.3.1. LC-141:链表是否有环 32 2.1.3.2. LC-142:环形链表入口 34 2.1.3.3. LC-876:链表的中间节点 37 2.1.3.4. LC-287:寻找重复数 40 2.2. 滑动窗口 43 2.2.1. 什么是滑动窗口 43 2.1.4. 常见题型 44 2.1.5. 注意事项 45 2.1.6. 滑动窗口模板 45 2.1.7. 滑动窗口相关题目 46 2.1.7.1. LC-3:无重复字符的最长子串 47 2.1.7.2. LC-76:最小覆盖子串 49 2.1.7.3. LC-209:长度最小的子数组 54 2.1.7.4. LC-239:滑动窗口最大 57 2.1.7.5. LC-395:至少有K个重复字符的最长子串 60 2.1.7.6. LC-567:字符串排列 62 2.1.7.7. LC-904:水果成篮 64 2.1.7.8. LC-424:替换后的最长重复字符 66 2.1.7.9. LC-713:乘积小于K的子数组 67 2.1.7.10. LC-992:K个不同整数的子数组 70 2.3. 左右指针 73 2.3.1. 模板 73 2.3.2. 相关题目 73 2.3.2.1. LC-76:删除倒数第N个节点 74 2.3.2.2. LC-61:旋转链表 76 2.3.2.3. LC-80:删除有序数组中的重复项 79 2.3.2.4. LC-86:分割链表 80 2.3.2.5. LC-438:找到字符串中所有字母的异位词 82 3. 模板 85 2.3.2.6. LC-76:删除倒数第N个节点 85

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_featherbrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值