我只是改写了我前一段时间问过的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)的最快方法是什么? 二进制搜索是一个选项吗?但由于输入不需要在数组中。我不知道我应该怎么做。
此外,如果有任何其他适合的数据结构请注释。
答案 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)
如果输入的数字在数组中,则二进制搜索将很方便。每次搜索失败时,指示数组中不存在该数字,索引low
和high
处的数组元素将为您提供范围。
答案 4 :(得分:1)
最有效(空间和时间)是将其实现为修改后的二进制搜索。
一个简单(但效率较低)的解决方案是用NavigableMap<Double, Double>
替换数组,并使用floorKey
和ceilingKey
来查找边界值。假设您使用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)。