
⚔️《二分查找王国大冒险》⚔️
------真正掌握二分,不再靠"改一改试一试"
第一章:勇者小明与"百万藏宝箱"
1、很久很久以前,在"算法大陆"里,有一个叫小明的小勇者。
有一天,国王交给他一个任务:
"这里有 1000000(100万)个宝箱,只有一个箱子里藏着黄金钥匙,你要尽快找到它!"
2、小明第一反应:
"那我一个一个找呀!"
于是:
第1个不是
第2个不是
第999999个还不是......
小明直接累趴。
3、这时候,汉克老师出现了:
"同学们,你们这样找太慢了。"
"你们需要学习一种超级强大的魔法------"
🌟二分查找!
第二章:什么叫"二分"?
1、汉克老师拿来一张按顺序写满数字的纸条:
1 2 3 4 5 6 7 8 9 10
现在要找数字 8。
(1)普通方法:
1个个看
(2)而二分法:
直接看中间!
(3)中间是谁?
5
因为:
8 > 5
(4)所以:
左边全部不要了!
瞬间砍掉一半!
(5)只剩:
6 7 8 9 10
(6)再看中间:
8
找到了!
2、🌟这就是二分的威力!
(1)每次:
砍掉一半!
(2)时间复杂度:
O(log n)
(3)100万个数:
只需要查大约20次!
第三章:真正的本质是什么?
1、很多同学以为:
"二分就是不断找中间。"
2、❌ 错!
二分真正的本质是:
🌟寻找"分界线"!
3、比如数组:
1 2 2 2 3
现在:
找第一个 >=2 的位置
4、我们把每个位置变成"是否满足条件"。
(1)条件:
a[i] >= 2
(2)于是得到:
1 2 2 2 3
❌ ✅ ✅ ✅ ✅
(3)你发现没有?
这里有一条神奇的边界:
❌ ❌ ❌ | ✅ ✅ ✅
(4)而二分查找:
🌟就是寻找这条边界!
第四章:二分最重要的三句话
汉克老师认真地说:
🌟第一句:你到底找什么?
你要找:
第一个满足?
最后一个满足?
还是随便一个?
不同目标:
模板不同!
🌟第二句:mid 是答案吗?
(1)如果:
mid可能是答案
(2)那就:
保留mid
(3)否则:
丢掉mid
🌟第三句:区间有没有变小?
二分最怕:
💀死循环!
原因只有一个:
区间没缩小!
第五章:死循环怪兽登场!
1、来看:
l = 2
r = 3
2、计算:
mid = (2+3)/2 = 2
3、如果你写:
l = mid;
会发生什么?
4、下一轮:
l还是2
r还是3
mid还是2
永远不变!
5、于是:
💀进入无限死循环!
第六章:mid 的两种身份
汉克老师在黑板上写下两个点:
1、🌟左中点
mid = (l+r)/2
例如:
2 和 3
mid:
2
偏左。
2、🌟右中点
mid = (l+r+1)/2
例如:
2 和 3
mid:
3
偏右。
3、🌟超级重要规律!
汉克老师讲到:
"谁接mid,mid就不能站谁那边!"
什么意思?
4、如果 mid 是左中点:
mid=(l+r)/2
那么:
不能 l=mid
因为 mid 可能等于 l!
会卡住!
5、如果 mid 是右中点:
mid=(l+r+1)/2
那么:
不能 r=mid
因为 mid 可能等于 r!
6、🌟一句口诀
左中点别写 l=mid
右中点别写 r=mid
第七章:五大二分神技
⚔️第一招:找第一个 ≥ x
1、比如:
1 2 2 2 3
找:
第一个 >=2
答案:
位置1
2、🌟思考
如果:
a[mid] >= x
说明:
mid可能是答案
所以:
r=mid
保留它!
3、🌟代码
int find(vector<int>& a,int x)
{
int l=0;
int r=a.size();
while(l<r)
{
int mid=l+(r-l)/2;
if(a[mid]>=x)
r=mid;
else
l=mid+1;
}
return l;
}
⚔️第二招:找第一个 > x
只需要改一个地方:
if(a[mid]>x)
⚔️第三招:找最后一个 ≤ x
这次:
满足条件继续往右找
所以:
l=mid+1
最后:
答案=l-1
⚔️第四招:找最后一个 < x
和上面几乎一样。
⚔️第五招:精确查找
这个大家最熟悉:
while(l<=r)
因为:
左右边界都可能是答案!
代码:
int find(vector<int>& a,int x)
{
int l=0;
int r=a.size()-1;
while(l<=r)
{
int mid=l+(r-l)/2;
if(a[mid]==x)
return mid;
else if(a[mid]<x)
l=mid+1;
else
r=mid-1;
}
return -1;
}
第八章:为什么有时候是 l<r?
1、很多同学最迷糊:
while(l<r)
和:
while(l<=r)
到底区别是什么?
🌟情况1:寻找边界
(1)例如:
-
第一个 ≥x
-
最后一个 ≤x
(2)这种:
答案一定存在于某个区间
(3)我们维护的是:
[l,r)
左闭右开区间。
(4)所以:
while(l<r)
(5)因为:
当 l==r 时
区间已经空了
搜索结束。
🌟情况2:精确找数字
(1)例如:
找有没有等于x
(2)此时:
每个点都可能是答案
(3)所以:
while(l<=r)
必须让最后一个点也检查。
第九章:真正的二分高手怎么思考?
真正厉害的人,
脑子里想的不是:
套哪个模板?
而是:
🌟第一步
条件是什么?
例如:
a[i]>=x
🌟第二步
哪些是❌?
哪些是✅?
例如:
❌ ❌ ❌ ✅ ✅ ✅
🌟第三步
我要找哪条边界?
例如:
第一个✅
🌟第四步
mid 要不要保留?
如果:
mid可能是答案
就保留!
否则丢掉!
第十章:终极口诀
汉克老师最后送给大家的口诀:
🌟二分查找四大口诀
🌟口诀1
二分不是找数字,
而是找边界!
🌟口诀2
先想分界线:
❌❌❌✅✅✅
再写代码!
🌟口诀3
mid包含与不包含:
mid可能是答案:
r=mid
mid不可能是答案:
l=mid+1
🌟口诀4
区间必须缩小!
否则:
💀死循环!
第十一章:课后挑战
1、🌟挑战1
数组:
1 3 3 3 5 7
请找:
第一个 >=3
答案是多少?
2、🌟挑战2
请找:
最后一个 <=3
3、🌟挑战3
为什么下面会死循环?
while(l<r)
{
int mid=(l+r)/2;
l=mid;
}
第十二章:最终奥义
真正掌握二分的人,
看到一道题时,
第一反应不是:
"我要背哪个模板?"
而是:
🌟"这里有没有单调性?"
🌟"我在找哪条边界?"
一旦学会这个思想:
你会发现:
二分答案
二分最大值
二分最小值
二分函数
全部都能打通!
因为:
🌟所有二分,本质都一样。