📜  二分搜索与包含Java列表中的性能(1)

📅  最后修改于: 2023-12-03 15:06:19.689000             🧑  作者: Mango

二分搜索与包含 Java 列表中的性能

二分查找是一种快速查找排序列表中指定元素的算法。它的时间复杂度为 $O(log_2{n})$,比线性查找的时间复杂度 $O(n)$ 要快得多。但是,在包含 Java 列表的情况下,对于较小的数据集,搜索指定元素的性能可能没有预期的那么好。让我们深入探讨这个问题。

二分搜索的实现

一个基本的二分查找实现如下所示:

public static int binarySearch(int[] arr, int target) {
    int start = 0;
    int end = arr.length - 1;

    while (start <= end) {
        int mid = (start + end) / 2;

        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] > target) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
    }

    return -1;
}

这个函数如果找到目标值,返回它在列表中的索引;否则,返回 -1。这个函数是一个基本的二分查找实现,它的时间复杂度为 $O(log_2{n})$。

二分搜索的性能

我们可以通过比较二分查找和线性查找的性能来看看二分查找在包含 Java 列表的时候表现如何。考虑以下代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class BinarySearchPerformance {

    private static final int ITERATIONS = 100000;
    private static final int MAX_LIST_SIZE = 1000;

    private static void runTest() {
        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < MAX_LIST_SIZE; i++) {
            list.add(i);
        }

        long binarySearchTime = 0;
        long linearSearchTime = 0;

        for (int i = 0; i < ITERATIONS; i++) {
            int target = (int) (Math.random() * MAX_LIST_SIZE);

            // Time binary search
            long start = System.nanoTime();
            Collections.binarySearch(list, target);
            long end = System.nanoTime();
            binarySearchTime += end - start;

            // Time linear search
            start = System.nanoTime();
            linearSearch(list, target);
            end = System.nanoTime();
            linearSearchTime += end - start;
        }

        System.out.println("Binary search time: " + TimeUnit.NANOSECONDS.toMillis(binarySearchTime) + "ms");
        System.out.println("Linear search time: " + TimeUnit.NANOSECONDS.toMillis(linearSearchTime) + "ms");
    }

    private static int linearSearch(List<Integer> list, int target) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) == target) {
                return i;
            }
        }

        return -1;
    }

    public static void main(String[] args) {
        runTest();
    }
}

这个程序生成一个包含 0 到 999 的整数的列表,并进行 $10^5$ 次搜索。每次搜索,它随机生成一个目标值并测量二分查找和线性查找的时间。

下面是运行该程序的示例输出:

Binary search time: 40ms
Linear search time: 2ms

我们可以看到,当列表较小时,线性查找的性能要优于二分查找的性能。这是因为线性查找的时间复杂度为 $O(n)$,如果列表的长度不超过一定的阈值,那么线性查找的时间复杂度可能比二分查找的时间复杂度还要低。

解决方法

有两个解决方案可以解决包含 Java 列表的二分搜索性能问题。

第一种方法是切换到线性查找,当数据集很小的时候,线性查找的效率优于二分查找。

第二种方法是使用数组而不是列表,因为数组具有更好的缓存友好性。在访问列表的元素时,Java 会在堆上分配新的内存。而对于数组,它们是在一个连续的内存块中进行分配的。这与计算机的缓存结构非常类似,因此数组可以利用CPU的高速缓存,从而更快地访问元素。

以下是在数组上实现的二分搜索算法的示例代码:

public static int binarySearch(int[] arr, int target) {
    int start = 0;
    int end = arr.length - 1;

    while (start <= end) {
        int mid = (start + end) / 2;

        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] > target) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
    }

    return -1;
}

请注意,这个函数接受一个整型数组并返回目标值在其中的索引。对于包含 Java 列表的情况,您可以使用 Arrays.asList() 将它转换为列表,然后使用集合算法来执行二分搜索。但是,在较小的数据集上,使用基本类型数组可能会带来更好的性能。

总结

尽管二分查找是一种快速查找排序列表中指定元素的算法,但在包含 Java 列表的情况下,它可能在较小的数据集上表现不佳。使用基本类型数组作为数据集可能会更快,并且当数据集很小时,线性查找可能比二分查找更快。根据数据集的大小和特点,可以选择适合的算法来实现最佳性能。