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

文章介绍了如何在有序数组中进行二分查找,包括C++和Python的实现方式,以及lower_bound和upper_bound的应用。文章首先解释了从无序数组到有序数组的排序过程,然后详细阐述了二分查找的原理和不同版本的实现,最后提供了C++和Python的示例代码来展示如何使用内置函数进行查找操作。
1086

被折叠的 条评论
为什么被折叠?



