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的实现与分析的更多相关文章

  1. Find Minimum in Rotated Sorted Array 典型二分查找

    https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ Suppose a sorted array is rot ...

  2. LC T668笔记 & 有关二分查找、第K小数、BFPRT算法

    LC T668笔记 [涉及知识:二分查找.第K小数.BFPRT算法] [以下内容仅为本人在做题学习中的所感所想,本人水平有限目前尚处学习阶段,如有错误及不妥之处还请各位大佬指正,请谅解,谢谢!] !! ...

  3. PHP实现文本快速查找 - 二分查找

    PHP实现文本快速查找 - 二分查找法 起因 先说说事情的起因,最近在分析数据时经常遇到一种场景,代码需要频繁的读某一张数据库的表,比如根据地区ID获取地区名称.根据网站分类ID获取分类名称.根据关键 ...

  4. 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 ...

  5. 终极二分查找--传说十个人写九个有bug

    之前写过一篇极为罗嗦的二分查找,非常得意地以为以后就可以避免踩坑了,但是今天才知道二分查找可以写的既简洁又鲁棒,唉!还是要多学习啊! 给一个按照从大到小的顺序排序好的数组a[]={1,2,3,4,7, ...

  6. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

  7. STL 二分查找三兄弟(lower_bound(),upper_bound(),binary_search())

    一:起因 (1)STL中关于二分查找的函数有三个:lower_bound .upper_bound .binary_search  -- 这三个函数都运用于有序区间(当然这也是运用二分查找的前提),以 ...

  8. Search for a Range——稍微升级版的二分查找

    Given a sorted array of integers, find the starting and ending position of a given target value. You ...

  9. 数据结构实验7:实现二分查找、二叉排序(查找)树和AVL树

    实验7 学号:      姓名:     专业: 7.1实验目的 (1) 掌握顺序表的查找方法,尤其是二分查找方法. (2) 掌握二叉排序树的建立及查找. 查找是软件设计中的最常用的运算,查找所涉及到 ...

随机推荐

  1. France \'98(概率)

    题目描述 Today the first round of the Soccer World Championship in France is coming to an end. 16 countr ...

  2. 实战weblogic集群之创建节点和集群

    一.启动weblogic,访问控制台 weblogic的domain创建完成后,接下来就可以启动它,步骤如下: $ cd /app/sinova/domains/base_domain/bin $ . ...

  3. 网络流(最大流) CQOI 2015 BZOJ 3931 网络吞吐量

    3931: [CQOI2015]网络吞吐量 Description 路由是指通过计算机网络把信息从源地址传输到目的地址的活 动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为路由器 ...

  4. getElementById返回的是什么?串讲HTML DOM

    1. getElementById()返回的是什么? 这个函数使用的最普遍,但是你有没有深入探究下,这个函数究竟返回的是什么么?我们来一起看看. var mydivEle = document.get ...

  5. PostgreSQL安装详细步骤(windows)

    原文地址:http://blog.chinaunix.net/uid-354915-id-3498734.html PostgreSQL安装:一.windows下安装过程安装介质:postgresql ...

  6. 使用Sublime Text搭建python调试环境

    原文地址:http://blog.csdn.net/wenxuansoft/article/details/38559731 pycharmt等IDE虽然用着爽,但毕竟在速度.资源上还是比较让人不爽的 ...

  7. tyvj P1431 [Tyvj Jan]分配任务(最大流)

    P1431 [Tyvj Jan]分配任务 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述      随着tyvj发展越来越大,管理员的任务越来越重,如何合理的 ...

  8. MVC的特点

    1.MVC模式 Mvc将应用程序分离为三个部分: Model:是一组类,用来描述被处理的数据,同时也定义这些数据如何被变更和操作的业务规则.与数据访问层非常类似. View:是一种动态生成HTML的模 ...

  9. Linux网络编程echo多线程服务器

    echo_server服务器多线程版本 #include <unistd.h> #include <stdlib.h> #include <stdio.h> #in ...

  10. CentOS 6.4的安装--史上最全-CRPER木木

    安装工具及软件: VmwareWorkstation9 CentOS-6.4-x86_64-LiveCD Vmware初始设置:           刚装好的VMWARE启动后,虽说默认已经设置好基础 ...