如何在数组中找到超过n / k次的所有值?

时间:2018-05-28 03:28:25

标签: arrays algorithm data-structures

我在Coursera上学习Algorithms, Part I课程,其中一个面试问题(未评级)如下:

  

十进制统治者。给定一个带有n个键的数组,设计一个算法来查找出现超过n / 10次的所有值。预期的   算法的运行时间应该是线性的。

它有一个提示:

  

使用quickselect确定第(n / 10)个最大密钥并检查它是否正确   发生超过n / 10次。

我不明白n / 10最大的密钥与n / 10重复值有什么关系。它不会告诉我哪些值超过n / 10次。

paper找到了更为通用的n / k解决方案,但我很难理解论文中的代码。

解决它的一种方法是对输入数组进行排序,然后再计算每个不同值的出现次数。这将花费O(nlogn)+ O(n)时间,这超出了问题的要求。

想法?

4 个答案:

答案 0 :(得分:2)

查找第n / 10个最大键(即,如果数组已排序,将位于n / 10位置的键)使用QuickSelect获取线性时间。如果此密钥的副本少于n / 10,那么您就知道按顺序排列的上面没有任何内容的n / 10个副本,因为在密钥之上的任何内容都没有n / 10个副本的空间按排序顺序。如果有不到10个或更多的副本,那么你发现的事情发生的次数超过n / 10次,并且再次发生的事情不会超过发生超过n / 10次的事情,因为没有&# 39; t它的空间。

现在,您有一个最多9n / 10值的数组,小于您刚从QuickSelect中找到的键。使用另一个QuickSelect传递从这个左上方的数组中找到关键字n / 10。和以前一样,你可能会发现一个发生n / 10次或更多次的密钥,无论你是否这样做,你都会从阵列中消除至少n / 10个条目。

因此,您可以使用10个QuickSelect调用搜索整个数组,每个调用都需要线性时间。 10是问题定义中固定的数字,因此整个操作仅计为线性时间。

答案 1 :(得分:1)

有一种Boyer-Moore投票算法的变体可以找到在n/kO(nk)中运行的输入中k = 10以上的所有元素。认为它应该在O(n * 10) = O(n)时间运行。

来自here

  

以下是一个有趣的O(nk)解决方案:我们可以解决上述问题   使用O(k-1)额外空间的O(nk)时间问题。请注意,有   从不超过输出中的k-1个元素(为什么?)。主要有   这个算法中的三个步骤。

     

1)创建一个大小(k-1)的临时数组来存储元素及其元素   count(输出元素将在这些k-1元素中)。   以下是临时数组元素的结构。

 struct eleCount {
     int element;
     int count; };  
 struct eleCount temp[];  This step takes O(k) time.
     

2)遍历输入数组并更新temp [](添加/删除一个   每个遍历元素的元素或增加/减少计数。该   array temp []在每一步都存储潜在的(k-1)候选者。这个   步骤需要O(nk)时间。

     

3)迭代最终(k-1)潜在候选人(存储在   温度[])。或每个元素,检查它是否实际计数超过   N / K。此步骤需要O(nk)时间。

     

主要步骤是步骤2,如何维持(k-1)潜在候选人   每一点?步骤2中使用的步骤就像着名的游戏:俄罗斯方块。我们   将每个数字视为俄罗斯方块中的一个部分,这些数字在我们的网站中落下   临时数组temp []。我们的任务是尽量保持相同的数字   堆叠在同一列上(临时数组中的计数递增)。

     

考虑k = 4,n = 9给定数组:3 1 2 2 2 1 4 3 3

     

i = 0

     3 _ _ temp[] has one element, 3 with count 1
     

i = 1

     3 1 _ temp[] has two elements, 3 and 1 with  counts 1 and 1 respectively
     

i = 2

     3 1 2 temp[] has three elements, 3, 1 and 2 with counts as 1, 1 and 1 respectively.
     

i = 3

     - - 2 
     3 1 2 temp[] has three elements, 3, 1 and 2 with counts as 1, 1 and 2 respectively.
     

i = 4

     - - 2 
     - - 2 
     3 1 2 temp[] has three elements, 3, 1 and 2 with counts as 1, 1 and 3 respectively.
     

i = 5

     - - 2 
     - 1 2 
     3 1 2 temp[] has three elements, 3, 1 and 2 with counts as 1, 2 and 3 respectively.  
     

现在问题出现了,当temp []时该怎么办   已满,我们看到一个新元素 - 我们删除了底行   元素堆栈,即我们将每个元素的数量减少1英寸   温度[]。我们忽略了当前元素。

     

i = 6

     - - 2 
     - 1 2  temp[] has two elements, 1 and 2 with counts as 1 and 2 respectively.
     

i = 7

       - 2 
     3 1 2  temp[] has three elements, 3, 1 and 2 with counts as 1, 1 and 2 respectively.
     

i = 8

     3 - 2
     3 1 2  temp[] has three elements, 3, 1 and 2 with counts as 2, 1 and 2 respectively. 
     

最后,我们最多有k-1个数字   温度[]。 temp中的元素是{3,1,2}。请注意计数   temp []现在没用了,只在步骤2中需要计数。现在我们   需要检查temp []中元素的实际计数是否更多   比不是n / k(9/4)。元素3和2的计数超过9/4。   所以我们打印3和2。

要获得此方法的正确证明,请查看this answer from cs.stackexchange

答案 2 :(得分:0)

你是对的。

在QuickSelect之后的下一个候选者中,十进制显性为:n / 10,2 * n / 10..9 * n / 10,因此仅检查n / 10索引是不够的

请注意,显性占据排序数组的长期运行,当然至少有一个具有所提及索引的元素属于该运行。

k = 3,N = 11的示例。让元素b占据阵列的至少1/3。在这种情况下,排序数组可能看起来像

b b b b * * * * * * * 
* b b b b * * * * * * 
* * b b b b * * * * *     
* * * b b b b * * * *     
* * * * b b b b * * *
* * * * * b b b b * *
* * * * * b b b b * *
* * * * * * b b b b *
* * * * * * * b b b b
      ^       ^       //positions for quickselect

请注意,在任何情况下,主导元素(如果k-dominant确实存在)占据至少一个标记位置。因此,经过两轮QuickSelect我们有两个候选人

答案 3 :(得分:-1)

说实话,我在这里没有任何答案,但是他们给出了解决问题的想法。根据提到的@MBo的答案,您对第10个元素使用快速选择,但不是使用简单的快速选择,而是使用基于3向快速排序的快速选择。您不仅将第10个元素放置在其位置上,还将所有与之相等的元素放置在该位置上。 一次迭代就足以计算出所有重复超过9个的元素。