我有一个int[]
数组,其中包含具有以下属性的值:
鉴于此列表,我想有效地找到数组中特定值的索引(或者如果该值不存在,则找到下一个更高的值)。
我已经已经实现了一个直接二分搜索,其中间隔二分法运行得相当好,但我怀疑数据的性质/分布可以被利用来更快地收敛到解决方案。
我对优化平均案例搜索时间感兴趣,但重要的是最坏情况永远不会比O(log n)更糟,因为数组有时非常大。
问题:在普通情况下,有可能比纯二进制搜索做得更好吗?
编辑(澄清其他问题/评论)
答案 0 :(得分:3)
这是在评论中,应该是一个答案。这是一项共同的努力,所以我将它作为CW答案:
您可能需要查看interpolation search。在最坏的情况下,他们可能比O(log n)
更糟糕,因此如果这是一个很难的要求,这将不适用。但是如果你的插值是合适的,根据数据分布,插值搜索可以击败直接二进制。
要知道,您必须使用相当智能的插值算法实现插值搜索,然后通过两者运行几个代表性数据集,以查看插值或二进制是否更适合。我认为它是两者中的一个,但我并不是真正的尖端搜索算法。
答案 1 :(得分:2)
我们在此处命名x
的间隔,并在搜索的数字中命名z
。
由于您希望均匀分布值,因此可以使用插值搜索。这类似于二分搜索,但将索引范围拆分为start + ((z - x[start]) * (end - start)) / (x[end] - x[start])
。
要获得O(log n)
的运行时间,您必须将插值搜索与二分搜索相结合(从二进制搜索开始,从交替插值搜索开始):
public int search(int[] values, int z) {
int start = 0;
int end = values.length-1;
if (values[0] == z)
return 0;
else if (values[end] == z) {
return end;
}
boolean interpolation = true;
while (start < end) {
int mid;
if (interpolation) {
mid = start + ((z - values[start]) * (end - start)) / (values[end] - values[start]);
} else {
mid = (end-start) / 2;
}
int v = values[mid];
if (v == z)
return mid;
else if (v > z)
end = mid;
else
start = mid;
interpolation = !interpolation;
}
return -1;
}
由于while循环的每次迭代都在二进制搜索中执行一步,因此它最多使用二进制搜索将使用的迭代次数的两倍(O(log n)
)。由于每个第二步都是插值搜索的一步,如果输入具有所需的属性,算法应该快速减小intervall大小。
答案 2 :(得分:0)
如果int []是
而不是搜索为什么不将值保存在其索引处。
假设数字是243而不是保存int [243] = 243中的值。
这样搜索将变得简单快捷。唯一剩下的就是找出更高的价值。
答案 3 :(得分:0)
我有一个解决方案
你说阵列可以是
1)数字在整个范围内均匀分布
2)有很长的连续数字序列。
所以,首先我们开始一个简单的测试,以确定它是type1还是type2
要测试类型1,
lenght = array.length;
range = array [length-1] - array [0];
现在考虑数字的值为
{长度(1/5),长度(2/5),长度(3/5),长度(4/5)},
如果数组分布是类型1,那么我们大致知道数组[i]的值必须是什么,所以我们检查4个位置以上是否接近已知值,如果它的分布相等。
如果它们接近,那么它的分布相等,所以我们可以很容易地找到数组中的任何元素。如果我们找不到基于上述方法的元素,我们认为它是类型2。
如果上面的测试失败,那么它是类型2 ,这意味着在数组中很少有地方存在连续数字的长序列。
所以,我们用二分法搜索来解决它。解释如下 *我们首先在数组的中间搜索,(比如说长度为2,索引为i)
左= 0,右=长;
的 BEGIN 强>:
I =(左+右)/ 2;
案例a.1 :我们的搜索号大于数组[i]
左= I;
*现在我们检查那个位置是否存在任何长的连续序列,即array [i],array [i + 1],array [i + 2]是连续的int。
案例a.1.1 :(如果它们是连续的),
因为它们是连续的,并且序列可能很长,我们根据搜索整数值直接搜索特定索引
例如,如果我们的搜索int是10,并且序列是5,6,7,8,9,10,11 15,100,103,
和array [i] = 5,然后我们直接搜索数组[i + 10-5],
如果我们找到我们的搜索int,则返回它,否则只从案例a.2继续[因为它显然会小于它]通过设置为as作为
right =(array [i + 10-5])
案例a.1.2,如果不是连续的话 从BEGIN继续;
案例a.2:我们的搜索号小于数组[i],
*案例a.2与a.1完全相似
*类似检查是否有任何后序,即数组[i-2],数组[i-1],数组[i]顺序,
如果它们是连续的序列,请像我们在a.1.1中所做的那样搜索精确值
如果它们不连续,则重复类似于案例a.1.2。
案例a.3 ,这是我们的搜索int,
然后归还。
希望这有助于