算法 | 二分查找、三分查找
有序数列查找特定大小数的算法:二分查找(Binary Search)
凸函数或者凹函数寻找极值的算法:三分查找(Ternary Search)
二分查找
分析
以递增数组为例,将其分为两半。若中间值大于要找的数,说明要找的数在左边一半;若中间值小于要找的数,说明要找的数在右边一半。不断将范围缩小一半,直到找到要找的数。
复杂度
时间复杂度:$O(\log n)$
($n$ 为数据规模)
代码实现
整数
- 查找 $\ge x$ 的第一个数:
(将 if (a[mid] >= x)
改为 if (a[mid] > x)
,可查找 $> x$ 的第一个数)
// a[ ]为储存数据的有序递增数组
// l ~ r为二分查找的数组范围
int l = 0, r = n - 1;
while (l < r)
{
int mid = (l + r) / 2;
if (a[mid] >= x)
r = mid;
else
l = mid + 1;
}
- 查找 $\le x$ 的最后一个数:
(将 if (a[mid] <= x)
改为 if (a[mid] < x)
,查找 $< x$ 的最后一个数)
易错点:此时 $mid$ 为 $(l+r+1)/2$,若忘记 $+1$ 将会死循环。
// a[ ]为储存数据的有序递增数组
// l ~ r为二分查找的数组范围
int l = 0, r = n - 1;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (a[mid] <= x)
l = mid;
else
r = mid - 1;
}
实数
查找满足一定条件的实数:(即以下代码令 check(x) == true 的 x)
// check(int x): 判断x是否满足条件
// eps: 精度(因为浮点数误差,不可直接比大小)
// BEGIN: 查找左边界
// END: 查找右边界
bool check(int x);
double eps = 1e-6;
double l = BEGIN, r = END;
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
algorithm 内函数
在有序递增数组中,查找 $\ge x(或> x)$ 的第一个数:
lower_bound(begin, end, num): 从数组的 begin 位置到 end - 1 位置二分查找第一个大于或等于 num 的数字,找到返回该数字的地址,不存在则返回 end。
upper_bound(begin, end, num): 从数组的 begin 位置到 end - 1 位置二分查找第一个大于 num 的数字,找到返回该数字的地址,不存在则返回 end。
在有序递减数组中,查找 $\le x(或< x)$ 的第一个数:
lower_bound(begin, end, num, greater\<type\>() ): 从数组的 begin 位置到 end - 1 位置二分查找第一个小于或等于 num 的数字,找到返回该数字的地址,不存在则返回 end。
upper_bound(begin, end, num, greater\<type\>() ): 从数组的 begin 位置到 end - 1 位置二分查找第一个小于 num 的数字,找到返回该数字的地址,不存在则返回 end。
将返回的地址减去起始地址,得到数字在数组中的下标:
int position = lower_bound(num, num + 100, 64) - num;
这样,num[position]
就是找到的第一个大于等于 64 的数字。
三分查找
分析
将 l ~ r 区间分为三份。如果 $f(m_1)<f(m_2)$,则极值点一定在区间 $(l,m_2)$;如果 $f(m_1)>f(m_2)$,则极值点一定在区间 $(m_1,r)$.
(只适用于区间内为单峰的函数)
时间复杂度
时间复杂度:$O(\log n)$
($n$ 为数据规模)
代码实现
// f(double x): 给定区间内的凹函数或凸函数
// eps: 精度(因为浮点数误差,不可直接比大小)
// BEGIN: 查找左边界
// END: 查找右边界
double f(double x);
double eps = 1e-6;
double l = BEGIN, r = END;
while(r - l > eps)
{
double m1 = l + (r - l) / 3, m2 = r - (r - l) / 3;
if(f(m1) > f(m2)) // 此为找最小值,若要找最大值,则改为<
l = m1;
else
r = m2;
}
本文采用 CC BY-SA 4.0 许可,本文 Markdown 源码:Haotian-BiJi