【问题标题】:Find the largest 10% of numbers in an array in order按顺序查找数组中最大的 10% 的数字
【发布时间】:2010-02-28 16:33:17
【问题描述】:

给定一个包含“N”个数字 (N >100) 的数组。我们如何才能按顺序找到其中最大的 10%? (如果n/10不是整数,可以四舍五入)

我想出了 3 种算法来尝试上述问题,但我不确定哪一种算法在渐近运行时间方面是最好的。我实际上可以进行任何修改以减少渐近时间吗?另外,如果 N 变得非常大,哪种算法可能仍然有效?

我列出了我对以下算法的想法,并且真的可以使用一些帮助来找出最有效的算法。

算法 1

我使用了选择排序,并在 10% 的数字被排序后停止。

算法 2

我建立了一个最大堆并不断删除最大的 10% 的数字

算法 3

还没有实现这个,但我的想法是使用任何顺序统计算法来找到包含前 10% 数字的分区,然后使用归并排序对它们进行排序。

【问题讨论】:

  • 数字是正整数还是也可以是双精度数?他们能有多大?
  • “按顺序”是什么意思?它们在原始来源中的顺序是升序还是降序?

标签: algorithm sorting


【解决方案1】:

最快的解决方案是使用partition-based selection algorithm,它在O(n) 中运行。它基于快速排序的思想,除了不是递归地对两个分区进行排序之外,您只需转到其中一个分区来查找k-th 最小元素。

通过搜索k=(90%*N)-th 最小的数字来找到最大的 10%。

如果您还记得快速排序中的分区是如何工作的,那么小于主元的元素将移至左侧,而其余元素则移至右侧。假设您要选择k-th 最小元素。然后,您会查看枢轴左侧是否至少有 k 元素。如果有,那么您知道您可以忽略正确分区中的元素。否则,您可以忽略左侧分区中的所有元素,因为您知道该元素将在右侧分区中。

请注意,选择算法仅识别前 10% 的数字是什么。如果您需要对它们进行排序,那么您必须对这些数字进行排序(但只有那些数字,其他 90% 可以忽略)。

【讨论】:

  • 一个很好的解决方案,但不是 O(n)。该问题要求“按顺序”排列前 10%,因此您需要做的不仅仅是选择一个元素。基于此问题的完整解决方案将是 O(n log n)。
  • 使用快速搜索和 O(n/10 log(n/10)) 对 10% 的初始元素进行排序,找到位于第 90 个百分位的数字是 O(n)在那个分区。这比 O(n log n) 对整个数组进行快速排序并获取前 n/10 项要好得多。要注意的另一件事是,如果数组中有重复的元素,则必须小心。
【解决方案2】:

算法 1: 选择排序将在 O(n^2) 中运行。第一次扫描你做 (n-1) 比较,第二次 (n-2),第 n/10 次 (n-n/10),所以 (n-1) + (n-2) + ... + (n-n /10) => O(n^2)

算法 2: 从堆中删除最大元素是 O(log n),所以这个将运行 O(n log n),因为你想删除 n/10 个元素。

另一种可能的算法,虽然仍然是 O(n log n),但我认为可能比 Algo-2 更好的是使用以下快速排序启发程序。

  1. 选择一个轴心点
  2. 扫描所有元素并将它们放入 2 个存储桶之一:那些小于枢轴的(左存储桶)和大于枢轴的(右存储桶) (n-1) 比较。遵循就地交换的快速排序过程。
  3. 一个。右边的桶大小 == n/10:你完成了。

    b.右边的桶大小 > n/10 那么新的列表就是右边的桶,用新的列表递归地进入第 1 步。

    c。右边的桶大小

【讨论】:

    【解决方案3】:

    我会使用对数组降序的快速排序并获得前 N/10 个项目。

    【讨论】:

    • 谢谢!很高兴知道,这是我想到的第一个也是最简单的方法。
    • 他可能忘记重新登录到他的新手帮助用户。
    • 我之前有一个评论,告诉我有更好的解决方案,但似乎已经被删除了。
    【解决方案4】:

    用前 n/10 个元素填充 O(lnN) 替换成本构造一个堆。扫描剩余的数字并与堆中的最小值进行比较。如果当前元素的值高于堆中的最小元素,则将其插入堆中并删除最小元素。在最坏的情况下,两次 O(lnN) 操作乘以 N 个扫描项目得到 O(N ln N),这在时间上并不比排序好,但比对所有内容进行排序需要更少的存储空间,因此在实践中可能会更快(特别是如果 N 个元素不适合缓存,但 n/10 会 - 渐近时间只在您处于平坦空间中时很重要)。

    【讨论】:

      【解决方案5】:

      最有效的算法是使用修改后的快速排序。

      快速排序首先选择一个“中间”值,然后将所有低于此值的值放在左侧,所有大于此值的值放在右侧。通常你会向下递归地对两边进行排序,但如果左边的元素少于 10%,你只需要对右边进行排序。

      如果超过10%,那么你只需要对左边进行排序,而且可能只对左边的部分进行排序。

      这不会将复杂度降低到最优 O(N lg N) 以下,但会降低常数因子,使其比明显的“快速排序然后选择前 10 个”方法更快。

      【讨论】:

        【解决方案6】:

        非常愚蠢的问题,只需使用任何排序算法对其进行排序,然后取前 N/10 项。

        Algo-2 等价于使用堆排序

        【讨论】:

          【解决方案7】:

          因为这是作业,所以我的答案是任何排序算法, 这是因为你无法在 O(n*log(n)) 下解决这个问题。

          如果这是可能的,那么您可以在 O(n*log(n)) 下对数组进行完全排序。 (通过在您想要完全排序的数组中找到已排序的前 10%,删除它们并重复此过程 10 次)。

          因为在 O(n*log(n)) 下无法进行排序,所以这个问题也是。

          【讨论】:

            【解决方案8】:

            如果您知道 N,只需创建一个长度为其 1/10 的数组。每个单元格的初始值是 Int.MinValue。检查数组中的每个数字。如果它大于百分之十数组中的最小数字,则添加它。

            避免排序,但代价是不断扫描答案数组。您可以通过保持排序顺序来抵消这一点,以便您可以使用二进制搜索。

            【讨论】:

            • “大于 10% 数组中的最小数”将是一个问题,如果初始数组是病态的,那么您将不得不在每次读取时不断地在 10% 中搜索下一个最小数
            • 是的,如果他使用迷你阵列,O(n^2)。原帖中带堆的 Algo-2 比这要好。
            • 我想如果排序是免费的,那么这确实很糟糕。哦等等...排序不是免费的。
            猜你喜欢
            • 2018-09-07
            • 1970-01-01
            • 2020-01-12
            • 2018-01-08
            • 1970-01-01
            • 2022-01-12
            • 2021-01-24
            • 2021-09-06
            • 2013-11-28
            相关资源
            最近更新 更多