目录
时间复杂度
空间复杂度
注意
二分搜索代码归纳
时间复杂度
怎么计算二分查找算法的时间复杂度呢?如果用T (n )来表示n 个有序元素的二分查找算法的时间复杂度,那么结果如下。
• 当n =1时,需要一次做比较,T (n )=O (1)。
• 当n >1时,将待查找元素和中间位置元素做比较,需要O (1)时间,如果比较不成功,那么需要在前半部分或后半部分搜索,问题的规模缩小了一半,时间复杂度变为T (n /2)。
• 当n >1时,可以递推求解如下:
递推最终的规模为1,令n =2 x ,则x =log n 。
二分查找的非递归算法和递归算法查找的方法是一样的,时间复杂度相同,均为O (logn )。
空间复杂度
在二分查找的非递归算法中,变量占用了一些辅助空间,这些辅助空间都是常数阶的,因此空间复杂度为O (1)。
二分查找的递归算法,除了使用一些变量,还需要使用栈来实现递归调用。在递归算法中,每一次递归调用都需要一个栈空间存储,我们只需看看有多少次调用即可。假设原问题的规模为n ,首先第1次递归就分为两个规模为n /2的子问题,这两个子问题并不是每个都执行,只会执行其中之一,因为与中间值做比较后,要么在前半部分查找,要么在后半部分查找;然后把规模为n /2的子问题继续划分为两个规模为n /4的子问题,选择其一;继续分治下去,在最坏情况会分治到只剩下一个数值,那么算法执行的节点数就是从树根到叶子所经过的节点,每一层执行一个,直到最后一层,如下图所示。
递归调用最终的规模为1,即n /2 x =1,则x =logn 。假设阴影部分是搜索经过的路径,一共经过了logn 个节点,也就是说递归调用了logn 次。递归算法使用的栈空间为递归树的深度,因此二分查找递归算法的空间复杂度为O (logn )。
注意
在二分搜索中需要注意以下几个问题。
(1)必须满足有序性。
(2)搜索范围。初始时,需要指定搜索范围,如果不知道具体范围,则对正数可以采用范围[0,inf],对负数可以采用范围[-inf,inf],inf为无穷大,通常设定为0x3f3f3f3f。
(3)二分搜索。在一般情况下,mid=(l +r )/2或mid=(l +r )>>1。如果l 和r 特别大,则为了避免l +r 溢出,可以采用mid=l +(r -l )/2。对判断二分搜索结束的条件,以及判断mid可行时是在前半部分搜索,还是在后半部分搜索,需要具体问题具体分析。
(4)答案是什么。在减少搜索范围时,要特别注意是否漏掉了mid点上的答案。
二分搜索分为整数上的二分搜索和实数上的二分搜索,大致过程如下。
二分搜索代码归纳
public class Main{
static int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10 };
// 下标:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
static int seek = 8;
public static void main(String[] args) {
// 大于某值的最小值
System.out.println(dichotomy1(0, array.length));
// 大于等于某值的最小值
System.out.println(dichotomy2(0, array.length));
// 小于某值的最大值
System.out.println(dichotomy3(0, array.length));
// 小于等于某值的最大值
System.out.println(dichotomy4(0, array.length));
// 实数二分
System.out.println(dichotomy5(0, array.length));
}
// 大于某值的最小值
static int dichotomy1(int left, int right) {
while (left < right) {
int mid = left + (right - left) / 2;
if (array[mid] > seek) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
// 大于等于某值的最小值
static int dichotomy2(int left, int right) {
while (left < right) {
int mid = left + (right - left) / 2;
if (array[mid] >= seek) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
// 小于某值的最大值
static int dichotomy3(int left, int right) {
while (left < right) {
int mid = left + (right - left + 1) / 2;
if (array[mid] < seek) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
// 小于等于某值的最大值
static int dichotomy4(int left, int right) {
while (right > left) {
int mid = left + (right - left + 1) / 2;
if (array[mid] <= seek) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
// 实数二分
static double dichotomy5(double left, double right) {
while (right - left > 1e-6) {
double mid = left + (right - left) / 2;
if (mid * mid * mid <= 20) {
left = mid;
} else {
right = mid;
}
}
return left;
}
}