我正在努力为以下问题找到一个好的算法:
目标是对输出进行分层,例如,在p = 3的情况下,我得到3个无序的小数,中数和大数列表(按此顺序)。
例如:
n = 10,p = 3
显然我可以在O(n*log(n))
时间通过简单排序然后分区来做到这一点,但我想知道这是不是可以在线性时间内完成。我知道QuickSelect在预期的O(n)
平均情况下运行,所以我的直觉是这个问题应该在O(p*n)
时间内解决。
天真地我认为你可以简单地运行QuickSelect p次,连续找到下一个第k个最小元素,然后对每个元素执行类似基数的排序,以通过原始步骤中识别的p个分支对元素进行分区
所以:
O(p*n)
O(p*n)
,我也不确定
这是一个最佳的复杂性(虽然我怀疑它是,因为它
似乎在p = 1和p = n)的边缘情况下工作有更好的算法吗?
由于
答案 0 :(得分:2)
QuickSelect实际上是一种分区算法,因此在QuickSelecting之后不需要额外的步骤。
假设我们有一个函数Partition(arr,lo,hi)返回一些k
,lo <= k < hi
并重新排列arr
,arr[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。它有一个很大的常数,请注意,如果您的输入是异常模式或来自潜在的敌对来源,请考虑它。