将列表分层为无序分区

时间:2015-05-03 17:29:20

标签: algorithm sorting

我正在努力为以下问题找到一个好的算法:

  • 输入:n个整数的未排序列表
  • 输出:p(大致)相等大小的未排序列表,其中每个列表的每个最小元素都大于其前面列表中的最大元素

目标是对输出进行分层,例如,在p = 3的情况下,我得到3个无序的小数,中数和大数列表(按此顺序)。

例如:

n = 10,p = 3

  • 输入:[4,1,8,7,9,3,6,0,2,5]
  • 输出:[[1,0,2],[4,3,6,5],[8,7,9]]

显然我可以在O(n*log(n))时间通过简单排序然后分区来做到这一点,但我想知道这是不是可以在线性时间内完成。我知道QuickSelect在预期的O(n)平均情况下运行,所以我的直觉是这个问题应该在O(p*n)时间内解决。

天真地我认为你可以简单地运行QuickSelect p次,连续找到下一个第k个最小元素,然后对每个元素执行类似基数的排序,以通过原始步骤中识别的p个分支对元素进行分区

所以:

  1. 我不确定我概述的算法是否正常工作
  2. 我不确定 确实需要O(p*n)
  3. 即使是O(p*n),我也不确定 这是一个最佳的复杂性(虽然我怀疑它是,因为它 似乎在p = 1和p = n)的边缘情况下工作
  4. 不是很好 优雅
  5. 有更好的算法吗?

    由于

2 个答案:

答案 0 :(得分:2)

QuickSelect实际上是一种分区算法,因此在QuickSelecting之后不需要额外的步骤。

假设我们有一个函数Partition(arr,lo,hi)返回一些klo <= k < hi并重新排列arrarr[i] <= arr[k] if {{1} {}}和i < k if arr[k] <= arr[i]。然后,从本质上讲,QuickSelect是:

k < i

这与QuickSort非常相似:

# After this call:
#   arr[i] <= arr[med] if lo <= i < med
#   arr[med] <= arr[i] if med < i < hi
QuickSelect(arr, lo, med, hi):
  if lo < hi:
    k = Partition(arr, lo, hi)
    if med < k:
      QuickSelect(arr, lo, med, k)
    else if k < med:
      QuickSelect(arr, k + 1, med, hi)

由于QuickSelect在指定点对数组进行分区(这不仅仅是找到相关元素),我们可以轻松地将Stratify定义为对QuickSelect的重复调用:

QuickSort(arr, lo, hi):
  if lo < hi:
    k = Partition(arr, lo, hi)
    QuickSort(arr, lo, k)
    QuickSort(arr, k + 1, hi)

由于QuickSelect为Stratify(arr, n, p): for i from 0 to p - 2 (inclusive): QuickSelect(arr, floor(i * n / p), floor((i+1) * n /p, n) ,因此上述分层为O(n)。只排序数组的选项需要O(p*n),因此如果O(n log n)不在p中,则上述分层很有用。 (由于O(log n)是一个很小的数字,实际上很可能排序是优越的。)

然而,很容易将分层结合到QuickSelect中,这是一种我们称之为QuickStratify的算法。 QuickStratify将QuickSort完成到数组分层的程度:

为方便起见,报告给定索引属于哪个层的函数:

log n

现在:

Stratum(i, n, p): floor(i * p / n)

我很确定QuickStratify是平均时间QuickStratify(arr, n, p, lo, hi): if Stratum(lo, n, p) < Stratum(hi, n, p): k = Partition(arr, lo, hi) QuickStratify(arr, n, p, lo, k) QuickStratify(arr, n, p, k + 1, hi) ,但我没有方便的证据,我可能错了。

答案 1 :(得分:1)

你的算法看起来不错。我唯一的狡辩是,我无法看到你如何能够执行类似基数的排序&#34;你说说。对于每个值x,您需要确定它进入哪个p槽,并且因为这些槽似乎没有非常特殊的结构(与常规基数排序不同,它们对应于某些固定的倍数)值)我认为你需要每个值进行O(log p)比较。

假设只使用比较,你不能比O(n log n)做得更好,因为如果可以的话,你可以通过设置p = n来排序n个优于O(n log n)的数字n并运行此算法。

另请注意,如果某个值可以多次出现,则生成的子集可能会被任意不平衡。 (如果你在你的条件下使用严格的&#34;大于&#34;这种可能性是不可避免的。)

最后,如果关注最坏情况的表现,则会有worst-case linear algorithm for selection。它有一个很大的常数,请注意,如果您的输入是异常模式或来自潜在的敌对来源,请考虑它。