从数组中查找数字的范围

时间:2010-08-19 09:11:18

标签: java arrays search

我只是改写了我前一段时间问过的question。 我有一个排序数组{2.0,7.8,9.0,10.5,12.3}

如果我输入了9.5 找到9.0和10.5以表明9.5在9.0和10.5之间(9.5> = 9.0和<10.5)的最快方法是什么? 二进制搜索是一个选项吗?但由于输入不需要在数组中。我不知道我应该怎么做。

此外,如果有任何其他适合的数据结构请注释。

7 个答案:

答案 0 :(得分:2)

二进制搜索肯定是“标准”方法 - http://en.wikipedia.org/wiki/Binary_search_algorithm。速度是O(log(N))而不是线性。

在某些特殊情况下,您可以做得比O(log(N))更好。但除非你正在处理真正巨大的数组大小满足这些特殊情况,否则你的二进制搜索确实是最快的方法。

答案 1 :(得分:2)

您可以使用Arrays.binarySearch快速找到9.0和10.0。

答案 2 :(得分:1)

我会这样做

double valuebefore = 0;
            double valueafter = 0;
            double comparevalue = 9;
            foreach (var item in a)
            {
                valueafter = item;
                if (item > comparevalue)
                {
                    break;
                }
                valuebefore = item;
            }

            System.Console.WriteLine("Befor = {0} After = {1}", valuebefore, valueafter);

答案 3 :(得分:1)

如果输入的数字在数组中,则二进制搜索将很方便。每次搜索失败时,指示数组中不存在该数字,索引lowhigh处的数组元素将为您提供范围。

答案 4 :(得分:1)

最有效(空间和时间)是将其实现为修改后的二进制搜索。

一个简单(但效率较低)的解决方案是用NavigableMap<Double, Double>替换数组,并使用floorKeyceilingKey来查找边界值。假设您使用TreeMap,这与二进制搜索具有相同的复杂性。

答案 5 :(得分:1)

这是我刚刚为您编写的二进制搜索算法,可以解决这个问题:

import java.util.Random;

public class RangeFinder {

    private void find(double query, double[] data) {

        if (data == null || data.length == 0) {
            throw new IllegalArgumentException("No data");
        }

        System.out.print("query " + query + ", data " + data.length + " : ");

        Result result = new Result();
        int max = data.length;
        int min = 0;
        while (result.lo == null && result.hi == null) {

            int pos = (max - min) / 2 + min;
            if (pos == 0 && query < data[pos]) {
                result.hi = pos;
            } else if (pos == (data.length - 1) && query >= data[pos]) {
                result.lo = pos;
            } else if (data[pos] <= query && query < data[pos + 1]) {
                result.lo = pos;
                result.hi = pos + 1;
            } else if (data[pos] > query) {
                max = pos;
            } else {
                min = pos;
            }
            result.iterations++;
        }
        result.print(data);
    }

    private class Result {

        Integer lo;
        Integer hi;
        int iterations;
        long start = System.nanoTime();

        void print(double[] data) {
            System.out.println(

            (lo == null ? "" : data[lo] + " <= ") +

            "query" +

            (hi == null ? "" : " < " + data[hi]) +

            " (" + iterations + " iterations in " +

            ((System.nanoTime() - start) / 1000000.0) + " ms. )");
        }
    }

    public static void main(String[] args) {

        RangeFinder rangeFinder = new RangeFinder();

        // test validation
        try {
            rangeFinder.find(12.4, new double[] {});
            throw new RuntimeException("Validation failed");
        } catch (IllegalArgumentException e) {
            System.out.println("Validation succeeded");
        }
        try {
            rangeFinder.find(12.4, null);
            throw new RuntimeException("Validation failed");
        } catch (IllegalArgumentException e) {
            System.out.println("Validation succeeded");
        }

        // test edge cases with small data set
        double[] smallDataSet = new double[] { 2.0, 7.8, 9.0, 10.5, 12.3 };
        rangeFinder.find(0, smallDataSet);
        rangeFinder.find(2.0, smallDataSet);
        rangeFinder.find(7.9, smallDataSet);
        rangeFinder.find(10.5, smallDataSet);
        rangeFinder.find(12.3, smallDataSet);
        rangeFinder.find(10000, smallDataSet);

        // test performance with large data set
        System.out.print("Preparing large data set...");
        Random r = new Random();
        double[] largeDataSet = new double[20000000];
        largeDataSet[0] = r.nextDouble();
        for (int n = 1; n < largeDataSet.length; n++) {
            largeDataSet[n] = largeDataSet[n - 1] + r.nextDouble();
        }
        System.out.println("done");
        rangeFinder.find(0, largeDataSet);
        rangeFinder.find(5000000.42, largeDataSet);
        rangeFinder.find(20000000, largeDataSet);
    }
}

答案 6 :(得分:0)

对于少量的垃圾箱,排序的链表将是最优雅的。你扫描它,当你找到一个更大的数字时,你就有了范围。

对于非常大的数字,值得将它们放在BTree或类似的树结构中以获得O(log(N))性能。

在Java中,您可以使用TreeSet。

lowerBound = boundaries.headSet(yourNumber).last();   upperBound = borders.tailSet(yourNumber).first();

对于大数字,

或类似的将是O(logN)。