二分查找是一种简单易懂的快速查找算法,时间复杂度也十分优秀,为O(logn),即使是2的32次方规模的大数据,最多也只需比较32次即可找到。
思想分析:二分查找运用了二分思想,在一个有序的数据集合中,每次都跟待查找的区间中点作比较,将待查找的区间缩小一半,直到找到待查找的元素,或者区间被缩小为0。下面给出简单实现的java代码。
public int binarySearch(int[] a, int value){
int low = 0;
int high = a.length-1;
while(low <= high){
//这里用到了位运算优化性能,你也可以写为
//low + (high-low) / 2
//(low + high)/2 这种写法遇到较大的数时容易溢出
int mid = low+((high-low)>>2);
if(a[mid] < value){
low = mid+1;
}else if(a[mid] > value){
high = mid - 1;
}else{
return mid;
}
}
return -1;
}
性能分析:二分查找的时间复杂度为什么为O(logn)呢?假设数据规模为n,每次数据可以缩小为一半,最坏情况下,我们需要区间被缩空时才能找到待找数据,设这时区间缩小了k次,也就是当n/2^k=1时,我们可以找到数据。因为每次缩小区间只需要一次比较操作,则时间复杂度为O(k),代入上面的式子,则时间复杂度为O(log(2)n),也就是O(logn)。
适用场景:
一、二分查找只适合可以按照下标随机访问的数据结构,说简单的就是数组。那么用链表可以吗?答案是否定的,链表不支持随机访问,你必须一个一个遍历才能到达待比较的中点。
二、二分查找只能查找有序数据。如果是无序的数组,必须先进行排序然后再进行查找。所以,二分查找不适用于频繁插入更新的场景,因为每次插入都可能造成失序,维护有序的成本过高。
三、二分查找不适合太大或太小数据规模的使用。当数据太大时,因为二分查找借助的是数组的顺序结构,而数组在内存空间中时需要连续的,这对内存的要求就比较严苛了(如你要查找的是1GB大小的数据规模,你就需要要求内存中有1GB的连续空间)。而当数据太小时,我们也只需要顺序遍历即可,二分查找无法突显其优势。
二分查找的几种变形
在上面的讲解中我们没有考虑数据有重复元素的情况,请看下面这几种情况。
查找第一个 值等于给定值 的元素
public int binarySearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 2);
if (a[mid] < value) {
low = mid + 1;
} else if (a[mid] > value) {
high = mid - 1;
} else {
//如果mid是第一个数或者mid前面的元素不为所查找值,则找到。
if ((mid == 0) || (a[mid - 1] != value))
return mid;
//否则将high设为mid-1继续查找
else high = mid - 1;
}
}
return -1;
}
查找最后一个 值等于给定值 的元素
public int binarySearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 2);
if (a[mid] < value) {
low = mid + 1;
} else if (a[mid] > value) {
high = mid - 1;
} else {
//如果mid是最后个数或者mid后面的元素不为所查找值,则找到。
if ((mid == a.length - 1) || (a[mid + 1] != value))
return mid;
//否则将low设为mid+1继续查找
else low = mid + 1;
}
}
return -1;
}
查找第一个 值大于等于给定值 的元素
public int binarySearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 2);
//注意判断条件
if (a[mid] >= value) {
//如果mid等于带一个数或者mid-1的数小于所查找的值
if((mid == 0)|| (a[mid - 1] < value))
return mid;
else high = mid - 1;
} else if (a[mid] < value) {
low = mid + 1;
}
}
return -1;
}
查找最后一个 值小于等于给定值 的元素
public int binarySearch(int[] a, int value) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 2);
if (a[mid] > value) {
high = mid - 1;
} else {
if ((mid == a.length - 1) || (a[mid + 1] > value))
return mid;
else low = mid + 1;
}
}
return -1;
}
本博文是学习王争老师的《数据结构与算法之美》课程的学习笔记,若有错误请指出,一起学习算法之美,谢谢!