二分查找里的upper bound与lower bound的实现与分析
1. 问题引入
最近参选了学堂在线的课程数据结构(2015秋)。课程由清华大学的邓俊辉老师主讲,在完成课后作业时,遇到了这样一个题目范围查询。在这个题目中,我需要解决这样一个子问题:给定了一组已经排好序的整数集合A[0...n]和一组闭区间[L,R],求这个整数集合中落在这个区间中的点的个数。
解决这个问题,我们很容易想到查找效率很高的二分查找,但是这又不是一般求key是否在一个数组里面的二分查找问题。对于区间左端点L,要找到数组里面大于或等于它的最小的元素的下标indexL。对于区间右端点R,要找到数组里面小于或等于它的最大的元素的下标indexR。
2. 具体实现
2.1 lower bound的实现
1. 首先考虑区间左端点L,我们要找出数组里面大于或等于L的最小值。
我们可以写出关于条件判断部分的伪代码:
if A[mid] >= key //那么数组A[begin,mid]里面一定有我们要求的元素
end = mid;
else //A[mid] < key时,我们所求的元素一定在数组A[mid+1,end]里面
begin = mid + 1;
2. 其次我们根据判断条件,可以写出中值mid的计算公式,这里的主要问题是取上界还是取下界。
这里考虑只还剩两个元素A[i,i+1]的情况。
如果取上界,即:mid = (begin+end+1)/2 = (i+i+1+1)/2 = i+1,那么如果满足条件A[mid]=A[i+1]>=key,要继续进行迭代的左分支区间A[begin,mid],仍相当于上一次的区间A[begin,end],由于区间规模不会出现缩减,这样左分支就会陷入死循环。如果满足条件A[mid]=A[i+1]<key,右分支区间A[mid+1,end],相当于区间A[end+1,end]直接由于不满足循环条件就退出了。总之,不能得出正确的判断结果。
如果取下界,即:mid = (begin+end)/2 = (i+i+1)/2 = i,那么如果满足条件A[mid]=A[i]>=key,要继续进行迭代的左分支区间A[begin,mid],相当于区间A[begin,begin],区间规模缩减到一,不会出现规模保持不变而出现死循环的现象。如果满足条件A[mid]=A[i]<key,右分支区间A[mid+1,end],相当于区间A[end,end],区间规模也缩减到一,不会出现死循环现象,可以继续向下处理。
因此,mid的计算公式应为取下界,即:mid = (begin+end)/2。
3. 考虑整个循环的结束条件。
我们可以接着第二步的分析继续进行。比如说仅剩两个元素A[i]=3,A[i+1]=7,而key=5。mid=(i+i+1)/2=i, A[mid]=A[i]=3<7,继而要在区间A[i+1,i+1]内进行进一步的判断。如果不退出循环,即循环的维持条件是begin<=end,而是进一步进行处理。mid=(i+1+i+1)/2=i+1, A[mid]=A[i+1]=7>=5,继而还在区间A[i+1,i+1]上做进一步处理。由于下次的begin=i+1, end=i+1,按照之前的循环维持条件begin<=end,不能退出循环,这样就会陷入死循环。而原本是可以得出结果的,大于或等于key值的最小元素的下标应该是i+1。所以我们的循环维持条件应该改为:begin < end。这样在处理区间A[i+1,i+1]时,由于begin=i+1, end=i+1,不满足循环维持条件begin<end,退出循环。但是我们不能直接返回此时begin的下标作为结果,因为有可能会出错。我们必须加以判断,如果begin这个下标对应的元素确实大于或等于key,那么begin就是对应的下标,否则,表示我们查找的元素不存在,我们可以返回-1,表示没找到。没找到对应的情况是数组A[begin,end]里的所有元素都小于key。
最终,我们循环维持的条件是:begin<end。
4. lower bound的实现代码
//二分查找大于或等于key值的最小的元素的下标
int binSearchLowerbound(int array[], int n, int key){
int begin = ;
int end = n - ;
//要能保证有正确的结果,循环的终止条件必须是:begin == end
while(begin < end){
//由于下面的判断条件产生了两个分支:1. [begin, mid] 2. [mid+1, end]
//mid的计算方法必须采用:mid = (begin+end)/2,即向下取整的方法
//才能保证两个分支都可以正常退出循环
int mid = begin + (end-begin)/;
if(array[mid] < key) //如果小于key值,则所找的元素一定位于区间[mid+1, end]中
begin = mid + ;
else //如果大于或等于key值,则所找的元素一定位于区间[begin, mid]中
end = mid;
}
//退出循环后有两种情况:1. 所查找的元素存在 2. 所查找的元素不存在,此时所有的元素都小于key
//因此要加以判断,如果存在,返回下标。如果不存在,返回-1,表示没找到
if (array[begin] >= key)
return begin;
else
return -;
}
2.2 upper bound的实现
分析过程同2.1的lower bound,下面只给出具体的实现代码
//二分查找小于或等于key值的最大值的下标
int binSearchUpperbound(int array[], int n, int key){
int begin = ;
int end = n - ;
//要能保证有正确的结果,循环的终止条件必须是:begin == end
while(begin < end){
//由于下面的判断条件产生了两个分支:1. [begin, mid-1] 2. [mid, end]
//mid的计算方法必须采用:mid = (begin+mid+1)/2,即向上取整的方法
//才能保证两个分支都可以正常退出循环
int mid = begin + (end-begin+)/;
if(array[mid] > key) //如果大于key值,则所查找的元素一定位于区间[begin, mid-1]中
end = mid - ;
else
begin = mid; //如果小于等于key值,则所查找的元素一定位于区间[mid, end]中
}
//退出循环后有两种情况存在:1. 所查找的元素存在 2. 所查找的元素不存在,此时所有的元素都大于key值
//因此要加以判断,如果存在,返回下标。如果不存在,返回-1,表示没找到
if (array[begin] <= key)
return begin;
else
return -;
}
3. 测试用的主程序
具体代码为:
#include <iostream>
using namespace std;
const int LEN = ;
int main(int argc, const char * argv[]) {
int A[LEN];
printf("请输入%d个已经排序的数:\n",LEN);
for (int i = ; i < LEN; i++)
cin >> A[i];
cout << "请输入要查找的左区间端点值:" << endl;
int L;
cin >> L;
int indexL = binSearchLowerbound(A, LEN, L);
printf("大于或等于%d的最小值的下标为%d\n", L, indexL);
cout << "请输入要查找的右区间端点值:" << endl;
int R;
cin >> R;
int indexR = binSearchUpperbound(A, LEN, R);
printf("小于或等于%d的最大值的下标为%d\n", R, indexR);
return ;
}
二分查找里的upper bound与lower bound的实现与分析的更多相关文章
- Find Minimum in Rotated Sorted Array 典型二分查找
https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ Suppose a sorted array is rot ...
- LC T668笔记 & 有关二分查找、第K小数、BFPRT算法
LC T668笔记 [涉及知识:二分查找.第K小数.BFPRT算法] [以下内容仅为本人在做题学习中的所感所想,本人水平有限目前尚处学习阶段,如有错误及不妥之处还请各位大佬指正,请谅解,谢谢!] !! ...
- PHP实现文本快速查找 - 二分查找
PHP实现文本快速查找 - 二分查找法 起因 先说说事情的起因,最近在分析数据时经常遇到一种场景,代码需要频繁的读某一张数据库的表,比如根据地区ID获取地区名称.根据网站分类ID获取分类名称.根据关键 ...
- leetcode-374-Guess Number Higher or Lower(二分查找)
题目描述: We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have t ...
- 终极二分查找--传说十个人写九个有bug
之前写过一篇极为罗嗦的二分查找,非常得意地以为以后就可以避免踩坑了,但是今天才知道二分查找可以写的既简洁又鲁棒,唉!还是要多学习啊! 给一个按照从大到小的顺序排序好的数组a[]={1,2,3,4,7, ...
- java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现
注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...
- STL 二分查找三兄弟(lower_bound(),upper_bound(),binary_search())
一:起因 (1)STL中关于二分查找的函数有三个:lower_bound .upper_bound .binary_search -- 这三个函数都运用于有序区间(当然这也是运用二分查找的前提),以 ...
- Search for a Range——稍微升级版的二分查找
Given a sorted array of integers, find the starting and ending position of a given target value. You ...
- 数据结构实验7:实现二分查找、二叉排序(查找)树和AVL树
实验7 学号: 姓名: 专业: 7.1实验目的 (1) 掌握顺序表的查找方法,尤其是二分查找方法. (2) 掌握二叉排序树的建立及查找. 查找是软件设计中的最常用的运算,查找所涉及到 ...
随机推荐
- 数学(矩阵乘法,随机化算法):POJ 3318 Matrix Multiplication
Matrix Multiplication Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 17783 Accepted: ...
- 【动态规划】Vijos P1616 迎接仪式
题目链接: https://vijos.org/p/1616 题目大意: 长度为N的字符串,只含‘j’和‘z’,可以将任意两个字符调换K次,求能够拥有的最多的'jz'串. 题目思路: [动态规划] 首 ...
- [置顶] C++中RTTI机制剖析
C++中要想在运行时获取类型信息,可没有Java中那么方便,Java中任何一个类都可以通过反射机制来获取类的基本信息(接口.父类.方法.属性.Annotation等),而且Java中还提供了一个关键字 ...
- 浅析Python中的struct模块
最近在学习python网络编程这一块,在写简单的socket通信代码时,遇到了struct这个模块的使用,当时不太清楚这到底有和作用,后来查阅了相关资料大概了解了,在这里做一下简单的总结. 了解c语言 ...
- isEqual,isEqualTostring,==三者的区别
isEqual:首先判断两个字对象的类型是否相同,在判断内容是否相同,如果类型不同直接return no.如先判断是否都是 NSString,在判断string的内容. isEqualTostring ...
- 操作SharedPreferences的注意点
如果使用SharedPreferences用于数据存取,大部分人喜欢使用如下代码: public void writeSharedprefs(int pos) { SharedPreferences ...
- 关于cocostudio加载UI json CCUIHELPER未声明问题
查看官方的文档,在文档的最后添加了如何加载项目.如下代码: UILayer* ul =UILayer::create(); ul->addWidget(CCUIHELPER->create ...
- Flash上传组件之SWFUpload文件上传
一.什么是SWFUpload? SWFUpload是一个客户端文件上传工具,最初由Vinterwebb.se开发,它通过整合Flash与JavaScript技术为WEB开发者提供了一个具有丰富功能继而 ...
- iOS 开发-单元测试
前言 维基百科对单元测试的定义如下: 在计算机编程中,单元测试(英语:Unit Testing)又称为模块测试, 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作.程序单元是应用的最小可 ...
- 第五章:最后一步准备,1.8的Json模型、状态描述机制详解
<基于1.8 Forge的Minecraft mod制作经验分享> 1.8的所有纹理材质都需要一个Json来对其描述,这一块感觉是各大神的教程里面涉及最少最浅的,我就斗胆在这分享下我研究了 ...