Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 我们可以使用二进制搜索来查找排序数组中最常出现的整数吗?_Arrays_Performance_Algorithm_Sorting_Frequency - Fatal编程技术网

Arrays 我们可以使用二进制搜索来查找排序数组中最常出现的整数吗?

Arrays 我们可以使用二进制搜索来查找排序数组中最常出现的整数吗?,arrays,performance,algorithm,sorting,frequency,Arrays,Performance,Algorithm,Sorting,Frequency,问题: 给定一个排序的整数数组,查找最常出现的整数。如果有多个整数满足此条件,则返回其中任何一个 我的基本解决方案: 扫描数组并跟踪每个整数的显示次数。因为它是被排序的,你知道一旦你看到一个不同的整数,你就得到了前一个整数的频率。跟踪哪个整数的频率最高 这是O(N)时间,O(1)空间解 我想知道是否有一种更有效的算法使用某种形式的二进制搜索。仍然是O(N)时间,但对于平均情况来说应该更快。最坏的情况不可能比O(N)时间更好。考虑每一个元素存在一次的情况,除了一个元素存在两次。为了找到该元素,您需

问题

给定一个排序的整数数组,查找最常出现的整数。如果有多个整数满足此条件,则返回其中任何一个

我的基本解决方案:

扫描数组并跟踪每个整数的显示次数。因为它是被排序的,你知道一旦你看到一个不同的整数,你就得到了前一个整数的频率。跟踪哪个整数的频率最高

这是O(N)时间,O(1)空间解


我想知道是否有一种更有效的算法使用某种形式的二进制搜索。仍然是O(N)时间,但对于平均情况来说应该更快。

最坏的情况不可能比O(N)时间更好。考虑每一个元素存在一次的情况,除了一个元素存在两次。为了找到该元素,您需要查看数组中的每个元素,直到找到它为止。这是因为知道任何数组元素的值都不会为您提供有关重复元素位置的任何信息,直到实际找到它为止。这与二进制搜索相反,在二进制搜索中,数组元素的值允许您排除许多其他元素。

否,在最坏的情况下,我们必须扫描至少n-2个元素,但请参见 下面是一个利用多个重复输入的算法

考虑一个对手,在第一次的n-3不同的探测中 n元素数组,返回m作为索引m处的值。现在是算法 知道数组看起来像

1 2 3 ... i-1 ??? i+1 ... j-1 ??? j+1 ... k-1 ??? k+1 ... n-2 n-1 n.
根据
s是什么,唯一正确的答案可能是
j-1
或者
j+1
,因此算法尚未完成

这个例子涉及一个数组,其中几乎没有重复项。在里面 事实上,我们可以设计一个算法,如果 发生n次中的k次,在数组中使用O((n/k)log k)探测。对于 j从ceil(log2(n))-1降到0,检查由 每(2**j)个元素。如果我们发现重复的,就停止。到目前为止的成本 是O(不适用)。现在,对于子数组中的每个元素,使用二进制搜索 查找其范围(O(n/k)在大小为O(k)的子数组中搜索,以获取总数 O((n/k)log k))的值

可以证明,所有算法都有一个最坏情况ω((n/k)log) k) ,使其在最坏情况下达到最佳,直到常数因子。

渐进(大oh值),您不能使用二进制搜索来改善最坏情况,原因是上面的答案已经给出。然而,这里有一些在实践中可能对你有帮助,也可能没有帮助的想法

对于每个整数,二进制搜索其最后一个匹配项。一旦找到它,您就知道它在数组中出现了多少次,并可以相应地更新计数。然后,从找到的位置继续搜索

如果只有几个元素重复了很多次,这是有利的,例如:

1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 3 3
因为您将只执行3次二进制搜索。但是,如果您有许多不同的元素:

1 2 3 4 5 6
然后您将执行
O(n)
二进制搜索,导致
O(n log n)
复杂性,因此更糟

这将为您提供比初始算法更好的最佳情况和更糟糕的最坏情况

我们能做得更好吗?我们可以通过在
i
位置查找最后一次出现的数字来改进最坏情况,如下所示:查看
2i
,然后查看
4i
等,只要这些位置的值相同。如果不是,请查看
(i+2i)/2

例如,考虑数组:

i
1 2 3 4 5 6 7 ...
1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 3 3
我们看一下
2i=2
,它具有相同的值。我们看
4i=4
,相同的值。我们看
8i=8
,不同的值。我们回溯到
(4+8)/2=6
。不同的价值观。回溯到
(4+6)/2=5
。相同的值。尝试
(5+6)/2=5
,值相同。我们不再搜索,因为我们的窗口宽度为1,所以我们完成了。从位置
6
继续搜索

这将改进最佳情况,同时尽可能快地保持最坏情况


渐近地,没有任何改进。要想知道它在实际应用中是否实际工作得更好,您必须对它进行测试。

二进制搜索可能无法工作,因为二进制搜索会消除剩余候选项的一半。有一些技巧可以用来避免读取数组中的每个元素。除非您的阵列非常长,或者出于好奇而解决问题,否则简单(线性扫描)的解决方案可能已经足够好了

以下是我认为二进制搜索不起作用的原因:从数组开始:给定中间项的值,您没有足够的信息从搜索中删除下半部分或上半部分

但是,我们可以多次扫描阵列,每次检查两倍的元素。当我们发现两个元素相同时,进行最后一次传递。如果没有其他元素重复,那么您已经找到了最长的元素运行时间(甚至不知道排序列表中有多少元素)。 否则,调查两个(或更多)较长的序列以确定哪个最长

考虑排序列表

Index 0 1 2 3 4 5 6 7 8 9 a b c d e f
List  1 2 3 3 3 3 3 3 3 4 5 5 6 6 6 7
Pass1 1 . . . . . . 3 . . . . . . . 7
Pass2 1 . . 3 . . . 3 . . . 5 . . . 7
Pass3 1 2 . 3 . x . 3 . 4 . 5 . 6 . 7
通过3后,我们知道3的运行必须至少为5,而任何其他数字的最长运行最多为3。因此,3是列表中出现频率最高的数字

使用正确的数据结构和算法(使用二叉树式索引),可以避免多次读取值。您还可以避免读取3(在第3遍中标记为x),因为您已经知道它的值

此解决方案具有运行时间
O(n/k)
,对于
k=1
,对于包含n个元素且运行时间最长的k个元素的列表,运行时间将降级为
O(n)
。对于小k,由于更简单的逻辑、数据结构和更高的RAM缓存,naive解决方案的性能会更好