lower_bound和upper_bound的用法

大兔子大兔子 提交于 2020-02-03 09:37:10

二分查找算法可以解决最简单的二分查找问题:a数组单调递增,并且其中没有重复的数值。我们遇到的实际问题可能就没有这么简单,可能会有重复的数值。比如a数组里有3个5。这时我们查找5就有一个问题:到底返回哪一个5的下标?

为了解决这些问题,C++ STL提供了两个特别好用的函数:lower_bound()和uppper_bound()。假设a是一个数组,n是数组长度,lower_bound(a, a+n, x)返回数组a[0]~a[n-1]中,大于等于x的数中,最小的数的指针。由于指针可以通过加减算偏移量,所以我们再减去a(数组名会被隐式转换成指针),就得到了相应的下标。我们看一下下面的例子,假设我们在a数组中找3这个数。
在这里插入图片描述
左边是a数组,当然这个a数组必须递增的,不然lower_bound()会出错。其中标红的是大于等于3的数。右边是lower_bound()的返回值减去a,是标红这些数里最小的一个的下标。注意最后一个例子,如果a数组中一个大于等于3的都没有,会返回数组长度n。

另一个函数叫做upper_bound()。同样假设a是一个数组,n是数组长度,upper_bound(a, a+n, x)返回数组a[0]~a[n-1]中,大于x的数中,最小的数的指针。

注意lower_bound是“大于等于”,upper_bound是“大于”。
我们看一下下面的例子,假设我们在a数组中找3这个数。
在这里插入图片描述
标红的部分是a数组中大于3的数。upper_bound的返回值减去a是这些数里最小的一个的下标。

其实对于lower_bound和upper_bound还有一个等价的解释。就是假设我要在a数组中插入x,然后还保持递增,那么lower_bound返回的是x最小的可以插入的数组位置,upper_bound返回的是x最大的可以插入的数组位置。
在这里插入图片描述
我们来通过一个程序看一下lower_bound和upper_bound的用法。
在这里插入图片描述
首先注意第一行,使用lower_bound和upper_bound需要包含algorithm头文件。然后注意第8~10行,lower_bound和upper_bound的返回值是整型指针。lower-a和upper-a是在做指针减法,结果是指针的偏移量,也就相当于是a数组的下标。程序运行的结果是最后注释的部分。

另外lower_bound和upper_bound的前两个参数是其实是待查范围的首尾指针(左闭右开区间,不包括尾指针),所以也可以写别的参数。比如下面的程序就是从a[1]开始查找:
在这里插入图片描述
上面我们介绍了lower_bound和upper_bound的用法。但是这两个函数是C++才有的。如果你平常用的是C或者Java怎么办呢?那我们只好写一个自己的my_lower_bound和my_upper_bound。首先我们定义一下my_lower_bound函数是这个样子:int my_lower_bound(int a[], int n, int x)

注意my_lower_bound和C++STL自带的lower_bound有区别,my_lower_bound的第一个参数是一个数组,第二个参数是数组长度,第三个参数是待查找的值;它的返回值是“大于等于x的数中,最小的数的下标“,另一个等价的说法是“假设在a数组中插入x,并保持插入后仍然有序,最小的合法插入下标”。

代码如下:

#include<cstdio>
#include<iostream>
using namespace std;
int n, x, a[100000];
int my_lower_bound(int a[], int n, int x){
	int l = 0;
	int r = n - 1;
	int ans = n;
	while(l <= r){
		int m = l + (r - l) / 2;
		if(a[m] >= x){
			ans = m;
			r = m - 1;
		}
		else
		    l = m + 1;
	}
	return ans;
}
int main()
{
	cin >> n >> x;
	for(int i = 0; i < n; i++)
	    cin >> a[i];
	int lower = my_lower_bound(a, n, x);
	cout << lower << endl;
	return 0; 
}

以上是my_lower_bound的代码,如果我们要实现my_upper_bound的代码,其实也很容易。my_lower_bound是找“大于等于x的数中,最小的数的下标“,所以第11行是if(a[m] >= x)。
my_upper_bound是找“大于x的数中,最小的数的下标“,所以只需要把第11行改成if(a[m]>x)即可。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!