在很多情况下,我们要在一个有序数组中找满足一定条件的数据或索引。比如,经常会用到,『找元素值大于等于某个数的最小值的索引』(lower_bound
)和『找元素值小于等于某个数的最大值的索引 + 1』(upper_bound
,其实也可以说成是『找元素值大于某个数的最小值的索引』)。用图像表示如下:
下面是一些预备工作。
上面提到的是『有序数组』,但是很多情况下我们拿到的数组是无序的,所以我们首先要将数组进行排序。
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)而得出的排序算法,它在实际使用中有很好的效率。
实现这种功能所用到的算法是『二分查找』。关于二分查找,网上有很多大佬写的总结,笔者也整理了自己的版本,戳我!!!其中关于二分查找的模板,笔者分为了三种:
口诀如下:
注:最后一句『相等』指的是『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 # 要大于才行,所以要加一
C++ 中已经有现成的函数,位于#include
中,其用法如下:
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
下面是一个简单的用法实例:
#include
#include
#include
#include
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
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