【二分搜索树】1、二分查找法的实现 - Binary Search

简单记录 - bobo老师的玩转算法系列–玩转算法 - 二分搜索树

二叉搜索树 Binary Search Tree

查找问题 Searching Problem

查找问题是计算机中非常重要的基础问题

二分查找法 Binary Search

v

<v v >v

对于有序数列,才能使用二分查找法 (排序的作用)

二分查找法的思想在1946年提出。
第一个没有bug的二分查找法在1962年才出现。

操作:实现二分查找法

非递归的二分查找算法 BinarySearch.java

package algo;

// 非递归的二分查找算法
public class BinarySearch {

    // 我们的算法类不允许产生任何实例
    private BinarySearch() {}

    // 二分查找法,在有序数组arr中,查找target
    // 如果找到target,返回相应的索引index
    // 如果没有找到target,返回-1
    public static int find(Comparable[] arr, Comparable target) {

        // 在arr[l...r]之中查找target
        int l = 0, r = arr.length-1;
        while( l <= r ){

            //int mid = (l + r)/2;
            // 防止极端情况下的整形溢出,使用下面的逻辑求出mid
            int mid = l + (r-l)/2;

            if( arr[mid].compareTo(target) == 0 )
                return mid;

            if( arr[mid].compareTo(target) > 0 )
                r = mid - 1;
            else
                l = mid + 1;
        }

        return -1;
    }

    // 测试非递归的二分查找算法
    public static void main(String[] args) {

        int N = 1000000;
        Integer[] arr = new Integer[N];
        for(int i = 0 ; i < N ; i ++)
            arr[i] = new Integer(i);

        // 对于我们的待查找数组[0...N)
        // 对[0...N)区间的数值使用二分查找,最终结果应该就是数字本身
        // 对[N...2*N)区间的数值使用二分查找,因为这些数字不在arr中,结果为-1
        for(int i = 0 ; i < 2*N ; i ++) {
            int v = BinarySearch.find(arr, new Integer(i));
            if (i < N)
                assert v == i;
            else
                assert v == -1;
        }

        return;
    }
}

使用递归地方式实现二分查找法

递归实现通常思维起来更容易。
递归在性能上会略差。

练习:实现二分查找法的递归实现

package algo;

// 递归的二分查找算法
public class BinarySearch2 {

    // 我们的算法类不允许产生任何实例
    private BinarySearch2() {}

    private static int find(Comparable[] arr, int l, int r, Comparable target){

        if( l > r )
            return -1;

        //int mid = (l+r)/2;
        // 防止极端情况下的整形溢出,使用下面的逻辑求出mid
        int mid = l + (r-l)/2;

        if( arr[mid].compareTo(target) == 0 )
            return mid;
        else if( arr[mid].compareTo(target) > 0 )
            return find(arr, l, mid-1, target);
        else
            return find(arr, mid+1, r, target);
    }

    // 二分查找法,在有序数组arr中,查找target
    // 如果找到target,返回相应的索引index
    // 如果没有找到target,返回-1
    public static int find(Comparable[] arr, Comparable target) {

        return find(arr, 0, arr.length-1, target);
    }

    // 测试递归的二分查找算法
    public static void main(String[] args) {

        int N = 1000000;
        Integer[] arr = new Integer[N];
        for(int i = 0 ; i < N ; i ++)
            arr[i] = new Integer(i);

        // 对于我们的待查找数组[0...N)
        // 对[0...N)区间的数值使用二分查找,最终结果应该就是数字本身
        // 对[N...2*N)区间的数值使用二分查找,因为这些数字不在arr中,结果为-1
        for(int i = 0 ; i < 2*N ; i ++) {
            int v = BinarySearch2.find(arr, new Integer(i));
            if (i < N)
                assert v == i;
            else
                assert v == -1;
        }

        return;
    }
}

比较

Main

package algo;

import algo.BinarySearch;
import algo.BinarySearch2;

// 比较非递归和递归写法的二分查找的效率
// 非递归算法在性能上有微弱优势
public class Main {

    private Main(){}

    public static void main(String[] args) {

        int N = 1000000;
        Integer[] arr = new Integer[N];
        for(int i = 0 ; i < N ; i ++)
            arr[i] = new Integer(i);


        // 测试非递归二分查找法
        long startTime = System.currentTimeMillis();

        // 对于我们的待查找数组[0...N)
        // 对[0...N)区间的数值使用二分查找,最终结果应该就是数字本身
        // 对[N...2*N)区间的数值使用二分查找,因为这些数字不在arr中,结果为-1
        for(int i = 0 ; i < 2*N ; i ++) {
            int v = BinarySearch.find(arr, new Integer(i));
            if (i < N)
                assert v == i;
            else
                assert v == -1;
        }
        long endTime = System.currentTimeMillis();

        System.out.println("Binary Search (Without Recursion): " + (endTime - startTime) + "ms");


        // 测试递归的二分查找法
        startTime = System.currentTimeMillis();

        // 对于我们的待查找数组[0...N)
        // 对[0...N)区间的数值使用二分查找,最终结果应该就是数字本身
        // 对[N...2*N)区间的数值使用二分查找,因为这些数字不在arr中,结果为-1
        for(int i = 0 ; i < 2*N ; i ++) {
            int v = BinarySearch2.find(arr, new Integer(i));
            if (i < N)
                assert v == i;
            else
                assert v == -1;
        }
        endTime = System.currentTimeMillis();
        System.out.println("Binary Search (With Recursion): " + (endTime - startTime) + "ms");

    }
}

D:\Environments\jdk-11.0.2\bin\java.exe -javaagent:D:\Java\ideaIU-2019.2.win\lib\idea_rt.jar=9455:D:\Java\ideaIU-2019.2.win\bin -Dfile.encoding=UTF-8 -classpath D:\IdeaProjects\imooc\Learning-Algorithms\05-Binary-Search-Tree\out\production\01-Binary-Search algo.Main
Binary Search (Without Recursion): 337ms
Binary Search (With Recursion): 514ms

Process finished with exit code 0

比较非递归和递归写法的二分查找的效率
非递归算法在性能上有微弱优势

posted @ 2020-02-24 23:56  渐若窥宏大  阅读(216)  评论(0编辑  收藏  举报