二分查找(折半查找)

二分查找的前提:数组中的数据必须是有序
核心思想:每次排除一半的数据,查询数据的性能明显提高很多

实现步骤
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))


posted @ 2024-01-31 10:14  狗狗没有坏心眼  阅读(6)  评论(0编辑  收藏  举报