Introduction to Algorithm, 3rd edtion, P206, problem 8-2-e
Suppose that the n records have keys in the range from 1 to k. Show how to modify counting sort so that the records can be sorted in place in O(n + k) time. You may use O(k) storage outside the input array. (Hint: How would you do it for k = 3?)
要求排序是in-place的,我的思路是一边遍历原序列,一边使用swap操作将元素换到正确的位置上。和非in-place的版本一样,使用一个count数组记录每个key出现的次数,再通过累加得到每个key的正确位置;从序列末尾开始往前遍历,将每个元素和其正序位置上的元素交换,然后将其key的计数减1。每次swap都会将一个元素移动到它的最终位置上,但是在遍历的过程中可能再次移动一个之前移动过的元素(因为key的计数值减小了,会错误地认为其不在正确位置上)。为了避免这种情况,在遍历时判断元素是否在正确的区间内,仅当不再正确区间中才对元素进行移动。Python实现的算法如下:
# countingsort.py
def countingsort(seq, k):
"""In-place counting sort.
"""
c = [0] * k
sn = len(seq)
for i in range(sn):
c[seq[i]] += 1
ac = c[:]
for i in range(1,k):
ac[i] += ac[i-1]
act = ac[:]
i = sn - 1
while i >= 0:
elem = seq[i]
# 该区间为排好序的序列中elem及所有相等的元素所处的位置
if ac[elem] - c[elem] <= i <= ac[elem] - 1:
act[elem] -= 1
i -= 1
continue
# 交换两元素,在下一次循环继续检查该位置
seq[i] = seq[act[elem]-1]
seq[act[elem]-1] = elem
act[elem] -= 1
# test
if __name__ == '__main__':
from random import randrange
k = 25
n = 30
testinput = [randrange(k) for x in range(n)]
countingsort(testinput, k)
assert testinput == sorted(testinput)
这个算法使用的额外空间为O(k)(两个长度为k的list),最后的while循环最多执行2n次,时间应该满足O(n+k),但是不再stable了。