问题定义
在区间[lo, hi)查找元素e
search语义约定:
如果成功,返回e的位置;如果失败,返回不大于e的最大元素的位置
只有版本C符合语义约定
版本A到版本B的改进是为了平衡向左和向右走时的查找次数,使得在最坏情况
下查找次数得到改善,而最好情况下版本A还是最好的(不过最好情况一般不会遇到)
版本B到版本C的改进是为了满足语义约定,因此最终版本C实现了最坏情况下更少的查找次数
以及search()的语义约定
版本A
循环条件:查找区间的元素至少有1个
判断条件:
e < val[mid]时向左, hi取mid;
val[mid] < e时向右,lo取mid+1;
除此以外返回当前位置
即[lo, mi) or [mid+1, hi) or mid hit
最终返回:表示查找失败,返回-1
实现
while (lo < hi) {
mi = (lo+hi) >> 1;
if (e < val[mid]) {
hi = mi;
} else if (val[mid] < e) {
lo = mid + 1;
} else {
return mid;
}
}
return -1;
版本B
循环条件:查找区间内元素至少有2个
判断条件:
e < val[mid]时向左,hi取mid;
val[mid] <= e时向右, lo取mid.
即[lo, mi) or [mi, hi)
最终返回:如果e == val[lo],返回lo;否则返回-1
版本C
循环条件:查找区间内元素至少有1个
判断条件:
e < val[mid]时向左,hi取mid; 使得 e < [hi, n)
val[mid]<=e时向右,lo取mid+1; 使得 [0, lo) <= e
最终返回:--lo;
当区间元素个数缩减至0时,lo-1是不大于e( <= e )的最大元素
实现
while (lo < hi) {
mid = (hi+lo)>>1;
if ( val[mid] <= e) {
lo = mid + 1;
} else if (e < val[mid]) {
hi = mid;
}
}
return --lo;
二分查找与STL lower_bound/upper_bound
值得注意的是,语义定于等价于upper_bound
参照版本C的思路,应该这样实现lower_bound的思路:
- 为了实现lower_bound, 需要同版本C,将整个区间划分为两部分,[0, lo) 内均
< e
;而[hi, n)内均e <=
. - 这样的话我们就可以返回hi的值作为lower_bound语义,或者为了计数方便(统计有多少个目标值e),也可以返回lo-1。
- 如果返回hi,语义是:不大于e的最小元素位置;如果返回lo-1,语义是:严格小于e的最大元素位置
int lower_bound(const vector<int> &nums, const int target) {
int lo = 0;
int hi = nums.size();
int mid = 0;
while (lo < hi) {
mid = (lo+hi) >> 1;
if (e <= nums[mid]) { // 这个if的判断以及hi的移动使得[hi, n)均 e <=
hi = mid;
} else ( nums[mid] < e){ // 这个else的判断及lo的移动使得[0,lo)均 < e
lo = mid + 1;
}
}
return hi;
}
其他优化
为了处理索引值使用int存储时可能出现的溢出情况,可以在计算mid时,使用
lo + ( (hi-lo)>>1 )
代替 (lo + hi) >> 1
处理数组索引的时候考虑一下数组长度就可以了
reference
标签:二分,val,lo,mid,查找,hi,深入,版本 来源: https://www.cnblogs.com/ijpq/p/15428293.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。