当前位置:   article > 正文

数据结构与算法【二分查找】Java实现_java代码实现二分查找

java代码实现二分查找

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

  • 如果找到返回索引
  • 如果找不到返回 -1

前提

给定一个内含 n 个元素的有序数组 A,一个待查值 target

1

设置 i = 0,j = n-1

2

如果 i > j,结束查找,没找到

3

设置 m = (i+j)/2 ,m 为中间索引并向下取整

4

如果 target < A[m] 设置 j = m - 1,跳到第2步

5

如果 A[m] < target 设置 i = m + 1,跳到第2步

6

如果 A[m] = target,结束查找,找到了

二分查找法的时间复杂度为 O(log(n))。 

代码实现基础版

  1. public class search_binary {
  2. public static void main(String[] args) {
  3. int[] a = {9, 18,21, 28, 32,40,46};
  4. int index = search(a, 32);
  5. System.out.println("查找结果为:" + index);
  6. }
  7. public static int search(int[] a, int target) {
  8. int i = 0;
  9. int j = a.length - 1;
  10. while (i <= j) {
  11. int m = (i + j) / 2;
  12. if (a[m] < target) {
  13. //说明要查找的数在右边
  14. i = m + 1;
  15. } else if (a[m] > target) {
  16. j = m - 1;
  17. } else {
  18. return m;
  19. }
  20. }
  21. return -1;
  22. }
  23. }

由于Java中int类型会自动向下取整,因此我们不需要考虑m带小数的情况。

查找过程如下

但是它仍存在一些问题。那就是Integer数值取值问题,m有可能会变为负数,情景再现。

  1. public static void main(String[] args) {
  2. int i = 0;
  3. int j = Integer.MAX_VALUE;//j表示int类型所能表达的最大数
  4. int m = (i + j) / 2;
  5. System.out.println(m);
  6. //假设目标值在m右侧
  7. i = m + 1;
  8. m = (i + j) / 2;
  9. System.out.println(m);
  10. }

1073741823

-536870912

之所以会出现这种问题,是因为m的值超出了int所能表达的最大值。在计算机中,一个数字最后都会由二进制表示,而int类型能表达的范围是-2147483648~2147483647,最大值转化为二级制为

可以看到第一位数字为0,这是因为Java中,是需要把二进制数字中的第一位数字当作符号位的(0为正,1为负)。超过最大值的二进制表示,第一位就会变为1,在Java中自然会被识别成一个负数。

为了避免这个问题,我们可以采用右移运算,也就是将所有的二进制右移一位(相当于除以2)。

具体代码实现如下

  1. public static int binarySearch(int[] a, int target) {
  2. int i = 0, j = a.length - 1;
  3. while (i <= j) {
  4. int m = (i + j) >>> 1;
  5. if (target < a[m]) { // 在左边
  6. j = m - 1;
  7. } else if (a[m] < target) { // 在右边
  8. i = m + 1;
  9. } else {
  10. return m;
  11. }
  12. }
  13. return -1;
  14. }

代码实现改动版 

  1. public static int binarySearch(int[] a, int target) {
  2. int i = 0, j = a.length;
  3. while (i < j) {
  4. int m = (i + j) >>> 1;
  5. if (target < a[m]) { // 在左边
  6. j = m;
  7. } else if (a[m] < target) { // 在右边
  8. i = m + 1;
  9. } else {
  10. return m;
  11. }
  12. }
  13. return -1;
  14. }

与基础版相比,改动版就是修改了 j 的边界值,将 j 的下次一次取值变为m。首先我们需要清楚的是,a[m]不是目标值,下标为m的数字是已经判断过了的,因此可以充当 j 的边界值。

流程如下,我们查找29的下标

但是这种实现方式存在一个问题,那就是目标值在a[m]右侧时,每次都需要先进行一次是否在a[m]左侧的操作。这就造成了每次不必要的查找,也会导致,查找左侧的元素的效率要比查找右侧的效率要高。对此,我们还可以进行优化。

具体代码如下

  1. public static int binarySearchBalance(int[] a, int target) {
  2. int i = 0, j = a.length;
  3. while (1 < j - i) {
  4. int m = (i + j) >>> 1;
  5. if (target < a[m]) {
  6. j = m;
  7. } else {
  8. i = m;
  9. }
  10. }
  11. return (a[i] == target) ? i : -1;
  12. }

while中的判断条件为元素中剩余查找的元素个数,当剩余1个时,将退出循环,然后在外部进行判断是否是目标值。

二分查找,数组中的重复元素,其实返回的值为第一次查找到的下标,如果添加了需求,要求返回重复元素的最左侧的下标就不能满足该需求,因此,我们还需要对该二分查找进行代码补充

  1. public static int binarySearch(int[] a, int target) {
  2. int i = 0, j = a.length - 1;
  3. int tempDate = -1;
  4. while (i <= j) {
  5. int m = (i + j) >>> 1;
  6. if (target < a[m]) {
  7. j = m - 1;
  8. } else if (a[m] < target) {
  9. i = m + 1;
  10. } else {
  11. //修改返回值处的代码
  12. tempDate = m; // 记录当前目标值的下标
  13. //查看左侧是否还存在相同的目标值
  14. j = m -1;
  15. }
  16. }
  17. return -1;
  18. }

Java中的二分查找

Java中的二分查找的实现在Arrays类中,对应代码如下

  1. private static int binarySearch0(int[] a, int fromIndex, int toIndex,
  2. int key) {
  3. int low = fromIndex;
  4. int high = toIndex - 1;
  5. while (low <= high) {
  6. int mid = (low + high) >>> 1;
  7. int midVal = a[mid];
  8. if (midVal < key)
  9. low = mid + 1;
  10. else if (midVal > key)
  11. high = mid - 1;
  12. else
  13. return mid; // key found
  14. }
  15. return -(low + 1); // key not found.
  16. }

与基础版所写基本没有差别,区别在于返回值。根据文档意思,这个返回值的意思是(-插入点)-1。意思是,如果没有查找到目标值,那么就会返回目标值应该插入数组中的下标值加一并取负数。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号