26java的 二分查找法 探索学习之路

二分法是用来查找数组中某个元素的索引,使用二分法之前必须保证数组已经排序(从大到小,从小到大均可)

二分法的原理很简单,我理解的二分查找法就是先把已经排序的数组(假设是从小到大)从中间分成两组,然后用目标数值与数组的中间数据相比较,如果目标数值比数组中间数据大,说明这个目标数值只可能存在于数组的后半部分,如果目标数值比数组中间数据小,说明这个目标数值只可能存在于数组的前半部分,假设目标数值比数组中间元素大,然后把数组的后半部分再分成两半,使用目标数值与现在的中间数值进行比较,依次类推,知道数组被分成两部分之后一边只有一个数值,如果此时目标数值和其中的一个相等就找到,不相等的话数组中就不存在这个目标数值。

但是我在写程序实现二分查找的时候遇到了一个问题,问题描述如下:我是用一个方法生成给定大小的整形数组,数组内容是随机生成的,然后使用一个方法实现数组的排序,然后准备写一个方法实现二分法查找给定目标数值的索引,在进行二分查找的时候需要对数组进行中间分半,此时就涉及到一个问题:究竟要进行多少次二分操作才能找到目标数值? 我想可能会用递归来进行二分操作,但是还是没有思路,写不出来,我决定看下JDK1.8的API文档中的binarySearch(int[],int)方法是如何实现二分查找的,
在这里插入图片描述
如上所示,API文档里面只给了如何使用这个方法,没有给出这个方法是如何实现的,那么就到Arrays类的源码看:

找到binarySearch(int[] ,int)方法,如下:

    /**
     * Searches the specified array of ints for the specified value using the
     * binary search algorithm.  The array must be sorted (as
     * by the {@link #sort(int[])} method) prior to making this call.  If it
     * is not sorted, the results are undefined.  If the array contains
     * multiple elements with the specified value, there is no guarantee which
     * one will be found.
     *
     * @param a the array to be searched
     * @param key the value to be searched for
     * @return index of the search key, if it is contained in the array;
     *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
     *         <i>insertion point</i> is defined as the point at which the
     *         key would be inserted into the array: the index of the first
     *         element greater than the key, or <tt>a.length</tt> if all
     *         elements in the array are less than the specified key.  Note
     *         that this guarantees that the return value will be &gt;= 0 if
     *         and only if the key is found.
     */
    public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, key);
    }

上面的文字清晰的介绍了binarySearch(int[] ,int)方法的功能和注意事项,这个方法直接返回了另一个方法,我们再看这个binarySearch0()方法,传递的参数中: a是已经排序的数组,key是目标数值,进入binarySearch0()方法:

    // Like public version, but without range checks.
    private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                     int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;//>>>无符号右移运算符,前面补0,数值除以2
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1; 
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

精彩!捋一下思路:首先标记被搜索数组的起止位置low、high,然后使用while循环来满足二分操作所需的循环条件,需要说明的是自始至终这里的while循环的条件都是满足的,而是使用return语句结束循环。下面看循环体,首先定义数组的中间索引和中间索引的数组成员,然后把目标数值和中间成员进行比较大小,如果目标数值大于中间成员,那么目标数值只可能出现在数组的后半部分(假设此时数组是从小到大排序),此时更新low的位置,low=mid+1,反之则更新high的位置,high=mid-1,如果此时目标数值等于中间成员,那不用继续比较了,直接输出此时的中间成员索引即为所求,如果搜索完毕还没找到目标数值,那就返回一个负数。

下面说一下这个二分查找法源码的可借鉴之处:对于问题:究竟要进行多少次二分操作才能找到目标数值? 我还准备用递归解决这个问题,后来发现是我把问题想复杂了。二分查找法的中心思想就是每次分半之后只比较数组中间数值和目标数值的大小,直到分半之后每边只剩一个数字,此时再进行一次比较便可知道数组中有没有目标数值,这种比较方法大大降低了运算复杂度。

下面是我写的实现二分查找法的程序:

package oop.OopDemo.binarySearch;

import java.util.Random;

public class SearchBinary_2 {
    private int[] value2;//封装
    private int index;

    public int[] getValue2() {
        return value2;
    }

    public void setValue2(int[] value2) {
        this.value2 = value2;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void initialize(int valueLength) {//初始化数组,使用随机数进行赋值
        Random random = new Random();
        for (int i = 0; i < valueLength; i++) {
            value2[i] = random.nextInt(100);
//            value2[i] = i*10;
        }
    }

    public void print() {//打印数组
        for (int i = 0; i < value2.length; i++) {
            System.out.print(value2[i] + "\t");
        }
        System.out.println();
    }

    public void sort() {//冒泡排序法对数组进行排序
        int temp;
        for (int i = 0; i < value2.length; i++) {
            for (int j = 0; j < value2.length - i - 1; j++) {
                if (value2[j] > value2[j + 1]) {
                    temp = value2[j + 1];
                    value2[j + 1] = value2[j];
                    value2[j] = temp;
                }
            }
        }
    }

    public int binarySearch(int destinationNum) {//二分查找法
        int low2 = 0;
        int high2 = value2.length - 1;
        while (low2 < high2) {
            int mid = (low2 + high2) / 2;
            int midNum = value2[mid];
            if (destinationNum > midNum)
                low2 = mid + 1;
            else if (destinationNum < midNum)
                high2 = mid - 1;
            else
                return mid;
        }
        return -(low2 + 1);
    }

    public static void main(String[] args) {
        SearchBinary_2 searchBinary_2 = new SearchBinary_2();
        searchBinary_2.initialize(20);
        searchBinary_2.print();
        searchBinary_2.sort();
        searchBinary_2.print();
        int num = searchBinary_2.binarySearch(67);
        System.out.println("目标数值的索引是:"+num);
    }
}

这个程序很容易理解,我只介绍一下数组的初始化方法initialize(int)方法,使用Random类的对象产生随机数,下面看JDK1.8 API关于Random类中nextInt(int)的介绍:(就是产生一组介于给定值之间的伪随机整数)

在这里插入图片描述

然后循环对value2数组进行赋值,一切看起来没啥问题,但是我一运行就报错:
在这里插入图片描述
我一开始以为是我使用Random类的对象的错误,检查修改之后再运行还是这个错误,于是乎我直接不用这个类,直接进行赋值,然而还是不行,显示同样的错误,错误显示就是在初始化数组的方法处,于是又开始了debug之路

  • 首先在出问题的地方设置断点,点击Debug
  • 程序进入initialize(int)方法,然后进入for循环,然后执行赋值语句
  • 神奇的是执行完赋值语句程序就break了(事实上我认为赋值语句就没有被执行)

现在可以完全确定问题是出在是赋值语句处了。捋一遍思路:initialize(int)方法 传递数组的长度参数,然后for循环根据这个数组长度参数进行循环遍历数组,结果数组还没开始遍历就结束了,那么问题只有一个:就是无法找到数组的索引! 思考之后我感觉可能是没有指定value2数组的长度就直接对value2数组进行赋值造成的问题,于是在初始化语句之前把value2数组长度初始化的语句加上,点击运行,欸!好了!

        int[] tempValue = new int[valueLength];
        setValue2(tempValue);

下面是运行结果:
在这里插入图片描述

我使用的是随机数生成的数组,所以能不能找到目标数值全靠缘分了。多运行几次说不定就能找到了,诺,找到了!
在这里插入图片描述

自学java,请多多指教!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值