二分探索メソッドの再帰的実装と非再帰的実装、および無視しやすいオーバーフロー(Java 実装)

序文

二分探索法、皆さんもご存知かと思います。これは、大学のデータ構造およびアルゴリズムコースにおける最初の数少ないアルゴリズムの 1 つです。その実装原理とコードは比較的単純です。このペーパーでは、再帰的と非再帰的の 2 つの方法を使用して二分探索アルゴリズムを実装します。アルゴリズム的にはオーバーフローしやすい部分が改善されているので、ぜひ注目していただければと思います。最後に二分探索アルゴリズムの下限と上限の実装についての考え方が残っていますので、興味のある方は勉強してみてください。集中してください对于有序序列,才能使用二分查找

非再帰的二分探索アルゴリズム

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 はターゲット要素の最初の出現を見つけ、ceil はターゲット要素の最後の出現を見つけます。
    • 目的の要素が見つからない場合、floor は目的の要素よりも小さい最も近い要素の位置を返し、ceil は目的の要素よりも大きい最も近い要素の位置を返します。

おすすめ

転載: blog.csdn.net/u011886447/article/details/105618672