【C++】从没见过这么详细的lower_bound的讲解

我这两个函数在牛客上做题见到,就充满疑惑这两个函数还有第二种接口??,我上C++官网去学习,例子就一个是最基础的,我看明白了。虽然是两个函数的接口就两个,但是有时候看别人使用的时候,里面参数还可以放不同的仿函数,我懵逼了。就去网上搜,但是大家讲解的都是它的第一个接口。我只能再把文档一遍一遍过,代码一遍遍的尝试,调试。
终于,我花了一天时间把这个函数才终于依靠着调试和 模仿着别人接口的使用,慢慢调弄明白(有可能我比较笨吧)

首先,大家都说用这两个函数之前必须是在有序的数组中,但是都没有说明为什么是在有序的数组,因为他们的底层实现是二分查找(这个也是我在别人的题解的时候知道的)
我们先来看lower_bound(后有lower_bound的底层实现,更容易理解lower_bound的原理)

lower_bound

函数原型:

原型1

template <class ForwardIterator, class T>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,  const T& val);

原型2

template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
模板参数解释:
  1. ForwardIterator就是一个迭代器,vector< int > v,v数组的首元素就是 v.begin()
  2. T&val , 就是一个T类型的变量
  3. Compare 就是一个比较器,可以传仿函数对象,也可以传函数指针
函数作用:

前提是有序的情况下,lower_bound返回指向第一个值不小于val的位置,也就是返回第一个大于等于val值的位置。(通过二分查找)

参数、返回值含义

first,last: 迭代器在排序序列的起始位置和终止位置,使用的范围是[first,last).包括first到last位置中的所有元素
val: 在[first,last)下,也就是区分(找到大于等于val值的位置,返回其迭代器)
comp: 主要针对于原型二,传一个函数对象,或者函数指针,按照它的方式来比较
返回值:返回一个迭代器,指向第一个大于等于val的位置

例子

原型一 例1

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
	vector<int> v= {3,4,1,2,8};
	//先排序
	sort(v.begin(),v.end()); // 1 2 3 4 8
	
	// 定义两个迭代器变量 
	vector<int>::iterator iter1;
	vector<int>::iterator iter2; 
	
	iter1 = lower_bound(v.begin(),v.end(),3);//迭代器指向3
	iter2 = lower_bound(v.begin(),v.end(),7);//迭代器指向8(因为第一个大于等于8)
	
	cout << iter1 - v.begin() << endl; //下标 2
	cout << iter2 - v.begin() << endl; //下标 4 
	system("pause");
}

总结:需要注意的是如果例子中(val >= 8),那么迭代器就会指向last位置,也就是数组尾元素的下一个,不管val多大,迭代器永远指向尾元素的下一个位置

原型二 例1
我们先来简单一点,和例一差不多的形式

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


int main()
{
	vector<int> v = { 3, 4, 1, 2, 8 };
	// 无序数组,不用sort排序,下面调用lower_bound排序

	// 定义两个迭代器变量 
	vector<int>::iterator iter1;
	vector<int>::iterator iter2;

	//less<int>() 是建小堆,排升序。
	//所以数组 v = {1,2,3,4,8} 
	//我们找第一个比1大的,那肯定就是首元素1了
	//我们找第一个比8大的,没有,所以就指向数组最后一个元素8了
	iter1 = lower_bound(v.begin(), v.end(), 1, less<int>());//
	iter2 = lower_bound(v.begin(), v.end(), 8, less<int>());//

	cout << iter1 - v.begin() << endl; //下标 所以就是 0
	cout << iter2 - v.begin() << endl; //下标 所以就是 4
	system("pause");
}

总结:我们原型2的第四个参数(比较器)可以用来排序,再来获取大于等于val值的迭代器

原型二 例2 仿函数传参

typedef struct Student
{
	int _id;  //学号
	int _num; //排名

	Student(int id, int num)
		:_id(id)
		, _num(num)
	{}
	
}Stu;

struct CompareV
{
	bool operator() (const Stu& s1,  const Stu& s2)//  排名升序
	{	
		return s1._num < s2._num;
	}
};

int main()
{
	vector<Stu> vS = { { 101, 34 }, { 103, 39 }, { 102, 35 } };


	//CompareV()排完序后是这个丫子
	//101 34
	//102 35
    //103 39
	auto iter = lower_bound(vS.begin(), vS.end(), Stu(200,33), CompareV());
	cout << iter - vS.begin() << endl; //我们就找到了按仿函数排序(找排名比33大的位置 就是0)
	system("pause");
}

我们了解了lower_bound的用法以后,我们再来了解一下lower_bound的原型实现 ----二分查找实现

lower_bound的底层实现
int lower_bound(vector<int>& nums, int x) {
	int left = 0;
	int right = nums.size() - 1;

	while (left <= right) {
		int mid = left +(right - left) / 2;
		if (x > nums[mid]) {
			left = mid + 1;
		}
		else {
			right = mid - 1;	
		}
	}
	return left;
}

总结:我们这里的实现,没用用迭代器去封装,但是把lower_bound的函数功能体现的淋漓尽致。在这里是不是有点恍然的感觉,原来底层就是二分查找搞得这么高大上。

lower_bound的算法应用见https://blog.csdn.net/weixin_43939593/article/details/105601631

upper_bound

用法和上面类似。只是把lower_bound的大于等于换成大于。仿函数等等全是相同的用法

底层实现
int upper_bound(vector<int>& nums, int x) {
	int left = 0;
	int right = nums.size() - 1;

	while (left <= right) {
		int mid = left +(right - left) / 2;
		if (x >= nums[mid]) {       //这里是大于等于
			left = mid + 1;
		}
		else {
			right = mid - 1;	
		}
	}
	return left;
}

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/105602530