欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > java数据结构与算法之二分查找(蓝桥杯)

java数据结构与算法之二分查找(蓝桥杯)

2024/12/22 1:09:09 来源:https://blog.csdn.net/weixin_65752158/article/details/144323084  浏览:    关键词:java数据结构与算法之二分查找(蓝桥杯)

二分查找算法也称折半查找,是一种非常高效的工作于有序数组的查找算法。后续的课程中还会学习更多的查找算法,但在此之前,不妨用它作为入门。

1.基础版

需求:在有序数组 A 内,查找值 target

  • 如果找到返回索引

  • 如果找不到返回 -1

算法描述

  • 对于一个算法来讲,都有较为严谨的描述,上面是一个例子

  • 后续讲解时,以简明直白为目标,不会总以上面的方式来描述算法

package demo;/*** 找的到返回索引* 找不到返回-1*/
public class Test1 {//查找元素5的下标public static void main(String[] args) {int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};int index = binarySearch(arr, 9);System.out.println(index);}private static int binarySearch(int[] arr, int number) {int left = 0;//左下标int right = arr.length - 1;//右下标while (left <= right) {// int mid = (left + right) / 2;//中间值//解决二个正整数相加/2不出现负数(溢出)才进行右移运算/*因为正整数相加/2不能动符号位,移位可以进行符号位,右移1位相当于除以2(向下取整)* */int mid = (left + right) >>> 1;//右移1位,相当于除以2(向下取整)if (arr[mid] == number) {return mid;} else if (arr[mid] > number) {right = mid - 1;} else {left = mid + 1;}}return -1;}
}
  • i,j 对应着搜索区间 [0,arr.length-1](注意是闭合的区间),left<=right 意味着搜索区间内还有未比较的元素,left,right 指向的元素也可能是比较的目标

    • 思考:如果不加left==right行不行?

      • 回答:不行,因为这意味着 left,right 指向的元素会漏过比较

  • m 对应着中间位置,中间位置左边和右边的元素可能不相等(差一个),不会影响结果

  • 如果某次未找到,那么缩小后的区间内不包含 mid

  • // int mid = (left + right) / 2;//中间值
    //解决二个正整数相加/2不出现负数(溢出)才进行右移运算
    /*因为正整数相加/2不能动符号位,移位可以进行符号位,右移1位相当于除以2(向下取整)
    * */
    int mid = (left + right) >>> 1;//右移1位,相当于除以2(向下取整)

2.改变版

public static int binarySearch(int[] a, int target) {int i = 0, j = a.length;while (i < j) {int m = (i + j) >>> 1;if (target < a[m]) {			// 在左边j = m;} else if (a[m] < target) {		// 在右边i = m + 1;} else {return m;}}return -1;
}
  • i,j 对应着搜索区间 [0,a.length)(注意是左闭右开的区间),i<j 意味着搜索区间内还有未比较的元素,j 指向的一定不是查找目标

    • 思考:为啥这次不加 i==j 的条件了?

    • 回答:这回 j 指向的不是查找目标,如果还加 i==j 条件,就意味着 j 指向的还会再次比较,找不到时,会死循环

  • 如果某次要缩小右边界,那么 j=m,因为此时的 m 已经不是查找目标了

为什么是j=m呢?

因为j最开始是指向不存在的元素的,如果写成j=m-1 ,那就可能m-1是我们要找的元素,就可能导致漏查。

3.平衡版

public static int binarySearchBalance(int[] a, int target) {int i = 0, j = a.length;while (1 < j - i) {int m = (i + j) >>> 1;if (target < a[m]) {j = m;} else {i = m;}}return (a[i] == target) ? i : -1;
}

思想:

  1. 左闭右开的区间,i 指向的可能是目标,而 j 指向的不是目标

  2. 不奢望循环内通过 m 找出目标, 缩小区间直至剩 1 个, 剩下的这个可能就是要找的(通过 i)

    • j - i > 1 的含义是,在范围内待比较的元素个数 > 1

  3. 改变 i 边界时,它指向的可能是目标,因此不能 m+1

  4. 循环内的平均比较次数减少了

4.Java 版

private static int binarySearch0(long[] a, int fromIndex, int toIndex,long key) {int low = fromIndex;int high = toIndex - 1;while (low <= high) {int mid = (low + high) >>> 1;long midVal = a[mid];if (midVal < key)low = mid + 1;else if (midVal > key)high = mid - 1;elsereturn mid; // key found}return -(low + 1);  // key not found.
}

long[] a 目标数组

int fromIndex为起始索引

int toIndex结束索引

long key代表要查找的目标元素值

  • 例如 [1,3,5,6] 要插入 2 那么就是找到一个位置,这个位置左侧元素都比它小

    • 等循环结束,若没找到,low 左侧元素肯定都比 target 小,因此 low 即插入点

  • 插入点取负是为了与找到情况区分

  • -1 是为了把索引 0 位置的插入点与找到的情况进行区分

5.Leftmost与 Rightmost

有时我们希望返回的是最左侧的重复元素,如果用 Basic 二分查找

  • 对于数组 [1, 2, 3, 4, 4, 5, 6, 7],查找元素4,结果是索引3

  • 对于数组 [1, 2, 4, 4, 4, 5, 6, 7],查找元素4,结果也是索引3,并不是最左侧的元素

public static int binarySearchLeftmost1(int[] a, int target) {int i = 0, j = a.length - 1;int candidate = -1;while (i <= j) {int m = (i + j) >>> 1;if (target < a[m]) {j = m - 1;} else if (a[m] < target) {i = m + 1;} else {candidate = m; // 记录候选位置j = m - 1;     // 继续向左}}return candidate;
}

有多个目标值时,采用candidate是记录索引的中间变量解决

如果希望返回的是最右侧元素

public static int binarySearchRightmost1(int[] a, int target) {int i = 0, j = a.length - 1;int candidate = -1;while (i <= j) {int m = (i + j) >>> 1;if (target < a[m]) {j = m - 1;} else if (a[m] < target) {i = m + 1;} else {candidate = m; // 记录候选位置i = m + 1;	   // 继续向右}}return candidate;
}

应用

对于 Leftmost 与 Rightmost,可以返回一个比 -1 更有用的值

Leftmost 改为

public static int binarySearchLeftmost(int[] a, int target) {int i = 0, j = a.length - 1;while (i <= j) {int m = (i + j) >>> 1;if (target <= a[m]) {j = m - 1;} else {i = m + 1;}}return i; 
}

返回i的含义是>=target最靠左的索引

Rightmost 改为

public static int binarySearchRightmost(int[] a, int target) {int i = 0, j = a.length - 1;while (i <= j) {int m = (i + j) >>> 1;if (target < a[m]) {j = m - 1;} else {i = m + 1;}}return i - 1;
}

返回i-1的含义是<=target最靠右的索引

搜索插入位置-Leetcode 35

参考答案1:用二分查找基础版代码改写,基础版中,找到返回 m,没找到 i 代表插入点,因此有

34

class Solution {public int[] searchRange(int[] a, int target) {int x=left(a,target);if(x==-1){return new int[]{-1,-1};}else{return new int[]{x,right(a,target)};}}public int left(int[]a,int target){int i=0;int j=a.length-1;int tmp=-1;while(i<=j){int m=(i+j)>>>1;if(a[m]>target){j=m-1;}else if(a[m]<target){i=m+1;}else{tmp=m;j=m-1;}}return tmp;}public int right(int[]a,int target){int i=0;int j=a.length-1;int tmp=-1;while(i<=j){int m=(i+j)>>>1;if(a[m]>target){j=m-1;}else if(a[m]<target){i=m+1;}else{tmp=m;i=m+1;}}return tmp;}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com