关于c++函数 lower_bound & upper_bound

关于c++ STL函数 lower_bound & upper_bound

前言

在刷leetcode的时候看到有用lower_bound(),对这个函数印象只有一点点。
到网上去查lower_bound() & upper_bound(),很多解释都是,lower_bound()返回第一个>=value的值,upper_bound()返回第一个>value的值。
这种结果为导向的答案,虽然能分清两个函数了,但作用还不太理解,以及为什么要取lower_bound(下边界) & upper_bound(上边界)这样两个名字。
于是我试着去理解一下这两个函数, 当然这两个函数也非常简单。

正文

a. 介绍

#include <algorithm>

template <class _ForwardIter, class _Tp, class _Compare>
inline _ForwardIter lower_bound(_ForwardIter __first, _ForwardIter __last,
                                const _Tp& __val, _Compare __comp) {}

template <class _ForwardIter, class _Tp, class _Compare>
inline _ForwardIter upper_bound(_ForwardIter __first, _ForwardIter __last,
                                const _Tp& __val, _Compare __comp) {}                

作为c++ STL中提供的函数,参数有4个。
first & last 是两个迭代器,代表序列的开始和结束。一般用容器的成员函数begin() & end()。
val是目标值。即要插入到序列中的值。
comp是比较函数。默认用less<type>

一般性举例

#include <vector>
#include <algorithm>

vector<int> vec;
int x;

vector<int>::iterator iter = lower_bound(vec.begin(), vec.end(), x);

b. 作用

函数的作用:将值x插入到目标序列v中,而不破坏原序列的顺序。
就有一个前提,这个序列是有顺序的,即排列好的。默认为升序。

返回值是一个迭代器iter,标识着一个可插入的位置。
这意味着,将x插入到iter的位置,将原本iter及之后的元素整体后移一位,就可以实现元素插入。

来自cppreference的介绍

std::upper_bound
Returns an iterator pointing to the first element in
the range [first, last) that is greater than value, or last if no such
element is found.

std::lower_bound
Returns an iterator pointing to the first element in
the range [first, last) that is not less than (i.e. greater or equal
to) value, or last if no such element is found.

c. 意义

不考虑边界情况
假设目标序列v已经排序,插入值为x。

  1. 序列v中不存在v[i] == x。
    函数返回序列v中第一个大于x的元素的位置,假设存在v[j] > x, v[j-1] < x。
    lower_bound()返回j的位置
    upper_bound()返回j的位置
  2. 序列v中有且仅有v[i] == x。
    序列v中有两个x可以插入,并且不破坏原序列的位置。
    1). 插在i位置的前面。即lower_bound(),返回i的位置。
    2). 插在i位置的后面。即upper_bound(),返回i+1的位置。
  3. 序列v中存在v[i] == x, a<= i<= b, a < b。
    为了不破坏a->b的序列,序列v中可插入的位置还是两个。
    1). 插在序列a->b的前面。即lower_bound(),返回a的位置。
    2). 插在序列a->b的后面。即upper_bound(),返回b+1的位置。

对于lower_bound来说,使用<进行比较。当v[i]<x时,继续向后移动;当第一次v[i]>=x时,说明到达了左边界。
对于upper_bound来说,使用<比较。当v[i]<x时,继续向后移动;当v[i]=x时,说明正在经历a->b序列;当第一次v[i]>x时,说明到达了右边界。

d. 举例

#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	vector<int> vec = {1, 2, 3, 4, [5, 5, 5, ]6, 7, 8}; //[]中序列代表可有可无
	int x = 5;
	
	auto iter1 = lower_bound(vec.begin(), vec.end(), x);
	/* 插入值x == 5
	 * 序列vec中值5的前面是值4,x应插在值4位置的后面,使得插入的x成为左边界
	 * 返回值即是值4的后一个位置
	 * 若关于[5]的序列存在,返回值是vec中第一个值5的位置
	 * 若关于[5]的序列不存在,返回值是vec中值6的位置
	 */

	auto iter2 = upper_bound(vec.begin(), vec.end(), x);
	/* 插入值x == 5
	 * 序列vec中值5的后面是值,x应插在值6位置的前面,使得插入的x成为右边界
	 * 返回值即是值6的位置
	 * 无论关于[5]的序列存在或不存在
	 */

	return 0;
}

e. 源码

源码来源SGI-STL v3.3

template <class _ForwardIter, class _Tp, class _Compare, class _Distance>
_ForwardIter __lower_bound(_ForwardIter __first, _ForwardIter __last,
                              const _Tp& __val, _Compare __comp, _Distance*)
{
  _Distance __len = 0;
  distance(__first, __last, __len); //len = last - first
  _Distance __half;
  _ForwardIter __middle;

  while (__len > 0) { //二分查找first->last
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half); //middle += half
    if (__comp(*__middle, __val)) { //middle < val
      __first = __middle;
      ++__first; //middle < val, middle的下一位, 即寻找最后一个middle<val的下一位
      __len = __len - __half - 1; //len = last - (middle + 1) = last - first
    }
    else //val <= middle, 相等时可能为目标位置,即保留
      __len = __half; //first + len == last, len即last
  }
  return __first; //返回值first,即目标值存储在first
}
template <class _ForwardIter, class _Tp, class _Compare, class _Distance>
_ForwardIter __upper_bound(_ForwardIter __first, _ForwardIter __last,
                           const _Tp& __val, _Compare __comp, _Distance*)
{
  _Distance __len = 0;
  distance(__first, __last, __len); //len = last - first
  _Distance __half;
  _ForwardIter __middle;

  while (__len > 0) { //二分查找first->last
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half); //middle += half
    if (__comp(__val, *__middle))
      __len = __half; //val < middle时,可能为目标值,即保留
    else {
      __first = __middle;
      ++__first; // middle <= val, middle的下一位, 即寻找最后一个middle<=val的下一位
      __len = __len - __half - 1;
    }
  }
  return __first; //返回值first,即目标值存储在first
}

综上,lower_bound()返回的位置,将使x成为有序序列v中一部份值的左边界;upper_bound()返回的位置,将使x成为有序序列v中一部份值右边界。
因为是实现是二分查找,时间复杂度o(log n)。以及快排的时间复杂度O (nlogn)。

STL容器set中,有成员函数std::set::lower_bound()与std::lower_bound(),因为set
的内部使用的是红黑树,其实现似乎与上述不同,具体还没有去看。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值