C++ / Python 关于 lower_bound & upper_bound 的算法实现与应用

33 篇文章 25 订阅
23 篇文章 4 订阅

Part.I Introduction

在这里插入图片描述

在很多情况下,我们要在一个有序数组中找满足一定条件的数据或索引。比如,经常会用到,『找元素值大于等于某个数的最小值的索引』(lower_bound)和『找元素值小于等于某个数的最大值的索引 + 1』(upper_bound,其实也可以说成是『找元素值大于某个数的最小值的索引』)。用图像表示如下:

在这里插入图片描述

下面是一些预备工作。

Chap.I 无序→有序

上面提到的是『有序数组』,但是很多情况下我们拿到的数组是无序的,所以我们首先要将数组进行排序。

C++ 排序函数:

sort(a.begin(),a.end(), cmp);

a根据规则cmp进行升序排列,执行完成之后a就变成升序的了;当要逆序时,用rbegin()即可。


Python 排序函数:

lst.sort(cmp=None, key=None, reverse=False)

一般不需给任何参数,换言之给个空括号即可;执行完之后lst就变成升序的了;如果要降序,只需将reverse赋值为true即可。


C++ 中的sort()函数用的是改进的快速排序算法,时间复杂度为O(logN);Python 中的sort()函数用的是Timsort算法, Timsort 是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法,它在实际使用中有很好的效率。

Chap.II 背后原理:二分查找

实现这种功能所用到的算法是『二分查找』。关于二分查找,网上有很多大佬写的总结,笔者也整理了自己的版本,戳我!!!其中关于二分查找的模板,笔者分为了三种:

  • 基础版:适用于无重复元素的数组
  • 寻找左侧边界版
  • 寻找右侧边界版

口诀如下:

  • 待查数组需有序,搜索区间两端闭;
  • while 条件带等号,否则补丁打不及。
  • if 相等便返回,其他杂事可不提;
  • mid 必须加减一,因为区间两端闭。
  • while 结束就GG,凄凄惨惨返-1;
  • 如果元素有重复,相等返回需注意。

注:最后一句『相等』指的是『if 相等』;『返回』指的是 while 结束之后的操作。


下面以Python 为例,自己实现这种功能。

lower_bound 实际上就是一个『寻找左侧边界版的二分查找』,其实现如下:

def lower_bound(nums, target):
    low, high = 0, len(nums) - 1
    pos = len(nums)
    while low <= high:
        mid = low + (high - low) // 2
        if nums[mid] < target:
            low = mid + 1
        else:
            high = mid - 1      # 和模版有所区别的是,这里对两种情况进行了一个合并
    return low

upper_bound 实际上就是一个『寻找右侧边界版的二分查找』,其实现如下:

def upper_bound(nums, target):
    low, high = 0, len(nums) - 1
    pos = len(nums)
    while low <= high:
        mid = low + (high - low) // 2
        if nums[mid] > target:
            high = mid - 1
        else:
            low = mid + 1       # 和模版有所区别的是,这里对两种情况进行了一个合并
    return high + 1             # 要大于才行,所以要加一

Part.II 应用示例

Chap.I C++

C++ 中已经有现成的函数,位于#include <algorithm>中,其用法如下:

  • auto itr = lower_bound(v.begin(), v.end(), tar);:在已排序的 v[begin,end)范围内查找值小于tar的元素,返回最后一个比tar小的元素的迭代器+1(第一个大于等于tar的元素的迭代器);
  • auto itr = upper_bound(v.begin(), v.end(), tar);:在已排序的 v[begin,end)范围内查找值小于等于tar的元素,返回第一个大于tar的迭代器。

可以看出来,返回的是一个迭代器,接着可通过下面的操作来得到我们所需:

cout << *itr << endl;	// 得到它的值
distance(v.begin(),itr)	// 得到它的索引下标,需要 #include<iterator>

下面是一个简单的用法实例:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    vector<int> v({1,2,2,2,3,6,8});
    int tar = 2;
    auto itr = lower_bound(v.begin(),v.end(),tar);
    cout << distance(v.begin(),itr) << endl;
    itr = upper_bound(v.begin(),v.end(),tar);
    cout << distance(v.begin(),itr) << endl;
    getchar();
    return 0;
}

输出:

1
4

Chap.II Python

Python 中也有现成的库函数,位于bisect中,需要导入相应的函数from bisect import bisect_right, bisect_left,其用法如下:

  • bisect_left(v, tar):在数组v中『找元素值大于等于某个数的最小值的索引』
  • bisect_right(v, tar):在数组v中『找元素值大于某个数的最小值的索引』

下面是一个简单的用法实例:

from bisect import bisect_left, bisect_right

v1=[1,2,2,2,3,6,8]
print(bisect_right(v1,2))
print(bisect_left(v1,2))

输出:

1
4
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流浪猪头拯救地球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值