二分查找的前提:数组中的数据必须是有序的
核心思想:每次排除一半的数据,查询数据的性能明显提高很多
实现步骤
1.定义两个变量,一个代表左边位置,一个代表右边位置
2.定义一个循环控制折半
3.每次折半,都算出中间位置处的索引
4.判断当前要找的元素值,与中间位置处的元素值的大小情况
往左边走,截止位置(右边位置)=中间位置-1
往右边走,起始位置(左边位置)=中间位置+1
中间位置处的元素值,正好等于我们要找的元素值
如果循环结束都没有,ruturn -1 特殊结果,代表没有找到数据,数组中不存在该数据
代码示例
public static int binarySearch(int[] arr,int target){
//设置左边位置
int left=0;
//设置右边位置
int right = arr.length-1;
//循环条件:如果左边位置小于等于右边位置
while(left<=right){
//中间位置等于(左边加右边)/2
int mid = (right + left) >>>1
//如果目标等于中间位置就返回
if(arr[mid] == target){
return mid;
}else if(arr[mid]< target){
//如果中间位置小于目标位置
//左边位置等于中间位置+1
left = mid +1;
}else if(arr[mid] > target){
//如果中间位置大于目标位置
//右边位置等于中间位置-1
right = mid-1;
}
}
//如果没有找到就返回 -1
return -1;
}
问题1:为什么判断条件是left<=right,而不是left<right
答案:因为他们所指向的元素也会参与比较,否则会少比较一轮
问题2:(left + right)/2 有没有问题
答案:数值较大时,两数相加会得到一个负数,可以用>>>(无符号运算符)解决
问题3:二分查找的最差情况会执行多少次(时间复杂度)
1.最差的查找情况中右边的比较次数会更多
最差的情况要么是目标元素不在数组中,要么目标元素在数组的最后一个位置
当目标元素不在数组中时,算法会一直缩小搜索范围,直到左边位置大于右边位置,这时算法才会停止。因此右边的比较次数会更多
当目标元素在数组的最后一个位置时,算法会进行一些列的比价操作,直到左边位置等于右边位置。因此,右边的比较次数也会更多
元素个数 | 循环次数 | |
---|---|---|
4-7 | 3 | floor(㏒2(4))=2+1 |
8-15 | 4 | floor(㏒2(8))=3+1 |
16-31 | 5 | floor(㏒2(16))=4+1 |
32-63 | 6 | floor(㏒2(32))=5+1 |
n | floor(㏒2(n))+1 |
循环次数 L=floor(㏒2(n))+1 | |
---|---|
left <= right | L+1 |
(right + left)>>>1 | L |
arr[mid]<target | L |
arr[mid] > target | L |
left = mid +! | L |
二分查找最差的执行情况
(floor(㏒2(n))+1)*5+4
如果有1024个元素
(floor(㏒2(1024))+1)*5+4=59
线性查找(和每一个数据进行比较)
public static int linearSearch(int[] arr, int target) {
// 遍历数组中的每个元素
for (int i = 0; i < arr.length; i++) {
// 如果找到目标值,返回索引
if (arr[i] == target) {
return i;
}
}
// 如果没有找到目标值,返回-1
return -1;
}
数据元素个数n | |
---|---|
int i=0 | 1 |
i<a.length | n+1 |
i++ | n |
a[i] == target | n |
return-1 | 1 |
线性查找最差执行情况
3*n+3
如果有1024个元素
3*1024+3=3075
Java中Arrays 类中的 binarySearch 方法是一个用于二分查找的静态方法
查找目标值 target 在数组中的索引。如果找到目标值,返回索引;如果未找到目标值,返回的索引将是一个负数,并且可以通过取负值再减一(-(index)-1)来获取插入点的索引。
示例:在数组中插入一个数据
public class Test{
public static void main(String[] args){
//原数组
int[] a ={2,4,5,8};
//要插入的目标值
int target=1;
//插入点
int index = Math.abs(Arrays.binarySearch(a,target)+1);
//新数组
int[] b = new int[a.length+1];
//先从原数组的插入位置复制到新数组
System.arraycopy(a,0,b,0,index);
//新数组的插入位置赋值为插入的目标值
b[index]=target;
//将原数组插入位置开始复制到新数组插入点+1位置,长度为原数组减去插入点
System.arraycopy(a,index,b,index+1,a.length-index);
System.out.println(Arrays.toString(b));
}
}
前面的函数有一点复杂
一般用大O表示法
线性查找示例
f(n)=3*n+3
g(n)=n
取c=4,n=3之后,g(n)可以作为f(n)的渐进上界,因此表示法写作O(n)
二分查找示例
f(n)=5*floor(㏒2(n))+9
g(n)=㏒2(n)
O(㏒2(n))