二分查找法的递归与非递归实现,以及容易忽视的溢出(Java实现)

前言

二分查找法,相信大家都不陌生。是大学的数据结构与算法这门课中,最先接触的几个算法之一。它的实现原理和代码都比较简单。本文用两种方式,递归和非递归的方式,来实现二分查找算法。算法中,对于容易出现溢出的地方,做了改进,希望大家注意。在最后,还留了思考,就是二分查找算法的floor和ceil的实现,有兴趣的可以研究一下。划重点,对于有序序列,才能使用二分查找

非递归的二分查找算法

package basic.bst;

/**
 * @Description:非递归的二分查找算法
 * @Author: during
 * @Date: 2020/4/18
 */
public class BinarySearch {
    
    

    private BinarySearch() {
    
    
    }

    /**
     * 二分查找法,在有序数组arr中,查找target
     * 如果找到target,返回相应的索引index;如果没有找到target,返回-1
     *
     * @param arr
     * @param target
     * @return
     */
    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;
            } else if (arr[mid].compareTo(target) > 0) {
    
    
                r = mid - 1;
            } else {
    
    
                l = mid + 1;
            }
        }

        return -1;
    }
}

递归的二分查找算法

递归实现通常思维起来更容易,但性能上略差。在后面的测试程序中,对二分查找法的递归和非递归实现也做了相应的对比。

package basic.bst;

/**
 * @Description:递归的二分查找算法
 * @Author: during
 * @Date: 2020/4/18
 */
public class BinarySearchRecursion {
    
    

    private BinarySearchRecursion() {
    
    
    }

    /**
     * 递归的二分查找法:在[l,r]的区间寻找target的值
     *
     * @param arr
     * @param l
     * @param r
     * @param target
     * @return
     */
    private static int find(Comparable[] arr, int l, int r, Comparable target) {
    
    

        if (l > r) {
    
    
            return -1;
        }

        // int mid = (l+r)/2;
        // 防止极端情况下(比如l和r都为整型的最大值)的整型溢出,使用下面的逻辑求出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
     *
     * @param arr
     * @param target
     * @return
     */
    public static int find(Comparable[] arr, Comparable target) {
    
    
        return find(arr, 0, arr.length - 1, target);
    }
}

测试二分查找算法

package basic.bst;

/**
 * @Description:测试
 * @Author: during
 * @Date: 2020/4/18
 */
public class Main {
    
    
    private Main() {
    
    
    }

    public static void main(String[] args) {
    
    

        int n = 10000000;
        Integer[] arr = new Integer[n];
        for (int i = 0; i < n; i++) {
    
    
            arr[i] = new Integer(i);
        }

        binarySearchTest(n, arr);
        binarySearchRecursionTest(n, arr);
    }

    /**
     * 测试非递归二分查找法:非递归算法在性能上有微弱优势
     */
    private static void binarySearchTest(int n, Comparable[] arr) {
    
    
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
    
    
            BinarySearch.find(arr, new Integer(i));
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Binary Search (Without Recursion): " + (endTime - startTime) + "ms");
    }

    /**
     * 测试递归二分查找法
     */
    private static void binarySearchRecursionTest(int n, Comparable[] arr) {
    
    
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
    
    
            BinarySearchRecursion.find(arr, new Integer(i));
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Binary Search (With Recursion): " + (endTime - startTime) + "ms");
    }
}

控制台打印结果:

Binary Search (Without Recursion): 1034ms
Binary Search (With Recursion): 1489ms

Process finished with exit code 0

拓展

  • floor(lower bound)和ceil(upper bound)
    • 上面写的二分查找法,当有重复元素时,不能保证找到的元素的索引是哪个索引。
    • 当target有多个,floor能找到target元素第一次出现的位置,ceil能找到target元素最后一次出现的位置。
    • 当没有找到target元素的时候,floor返回比target元素小且最近的元素位置,ceil返回比target元素大且最近的元素位置。

猜你喜欢

转载自blog.csdn.net/u011886447/article/details/105618672
今日推荐