如果我们只对整数进行排序,我们可以使用计数排序的原位变体,其空间复杂度为O(k),与变量n 无关。换句话说,当我们将k 视为常数时,空间复杂度为O(1)。
或者,我们可以使用in place radix sort 和lg k 的二元分区阶段和O(lg k) 空间复杂度(由于递归)。甚至更少的阶段使用计数排序来确定 n 路分区的桶边界。这些解决方案的时间复杂度为 O(lg k * n),仅以变量 n 表示时为 O(n)(当 k 被视为常数时)。
当k 被认为是常数时,获得O(n) 步复杂度和O(1) 空间复杂度的另一种可能的方法是使用可以称为减法排序的东西,正如OP 在他们的own answer 中所描述的那样,或elsewhere。它具有步复杂度O(sum(input)),优于O(kn)(对于某些特定的输入,它甚至优于二进制基数排序的O(lg k * n),例如对于[k, 0, 0, ... 0] 形式的所有输入)和空间复杂度O(1) .
另一种解决方案是使用bingo sort,其步长复杂度为O(vn),其中v <= k 是输入中唯一值的数量,空间复杂度为O(1)。
请注意,这些排序解决方案都不是稳定的,如果我们排序的不仅仅是整数(一些具有整数键的任意对象),这很重要。
paper 中还描述了一种尖端的稳定分区算法,空间复杂度为O(1)。将其与基数排序相结合,可以构造出一种具有恒定空间的稳定线性排序算法——O(lg k * n)步复杂度和O(1)空间复杂度。
编辑:
根据评论的要求,我试图找到计数排序的“原位”变体的来源,但没有找到任何可以链接到的优质内容(真的很奇怪对于这样的基本算法,没有容易获得的描述)。因此,我在这里发布算法:
常规计数排序(来自维基百科)
count = array of k+1 zeros
for x in input do
count[key(x)] += 1
total = 0
for i in 0, 1, ... k do
count[i], total = total, count[i] + total
output = array of the same length as input
for x in input do
output[count[key(x)]] = x
count[key(x)] += 1
return output
假设输入由一些对象组成,这些对象可以由0 到k - 1 范围内的整数键标识。它使用O(n + k) 额外空间。
整数的简单原位变体
这个变体要求输入是纯整数,而不是带有整数键的任意对象。它只是从计数数组中重构输入数组。
count = array of k zeros
for x in input do
count[x] += 1
i = 0
for x in 0, 1, ... k - 1 do
for j in 1, 2, ... count[x] do
input[i], i = x, i + 1
return input
它使用O(k) 额外空间。
具有整数键的任意对象的完整原位变体
这个变体类似于常规变体接受任意对象。它使用交换将对象放置在适当的位置。在前两个循环中计算 count 数组后,它使其保持不变,并使用另一个名为 done 的数组来跟踪有多少具有给定键的对象已经放置在正确的位置。
count = array of k+1 zeros
for x in input do
count[key(x)] += 1
total = 0
for i in 0, 1, ... k do
count[i], total = total, count[i] + total
done = array of k zeros
for i in 0, 1, ... k - 1 do
current = count[i] + done[i]
while done[i] < count[i + 1] - count[i] do
x = input[current]
destination = count[key(x)] + done[key(x)]
if destination = current then
current += 1
else
swap(input[current], input[destination])
done[key(x)] += 1
return input
此变体不稳定,因此不能用作基数排序中的子程序。它使用O(2k) = O(k) 额外空间。