使用计数排序算法时,将创建一个列表,并使用其索引作为键,同时将整数出现的次数添加为列表中的值。为什么这与简单地创建一个以keys
作为索引并以counts
作为值的字典不同?如:
hash_table = collections.Counter(numList)
或
hash_table = {x:numList.count(x) for x in numList}
创建哈希表后,基本上只需将整数出现的次数复制到另一个列表中即可。哈希表/字典的查找时间为O(1),因此如果您仅引用键/值对,为什么这不是首选?
我将下面的计数排序算法包括在内以供参考:
def counting_sort(the_list, max_value):
# List of 0's at indices 0...max_value
num_counts = [0] * (max_value + 1)
# Populate num_counts
for item in the_list:
num_counts[item] += 1
# Populate the final sorted list
sorted_list = []
# For each item in num_counts
for item, count in enumerate(num_counts):
# For the number of times the item occurs
for _ in xrange(count):
# Add it to the sorted list
sorted_list.append(item)
return sorted_list
答案 0 :(得分:4)
您当然可以做这样的事情。问题是这样做是否值得。
计数排序的运行时间为O(n + U),其中n是数组中元素的数量,U是最大值。请注意,随着U变得越来越大,该算法的运行时间开始明显下降。例如,如果U> n并且我在U上再增加一位(例如,将其从1,000,000更改为10,000,000),则运行时间可以增加十倍。这意味着随着U变得越来越大,计数排序开始变得不切实际,因此通常在U相当小时运行计数排序。如果您要使用较小的U值进行排序排序,那么使用散列表不一定是值得的。散列项比仅进行标准阵列查找要花费更多的CPU周期,对于小型阵列而言,潜在的内存节省可能不值得花费额外的时间。而且,如果您使用非常大的U值,则最好切换到基数排序,这实际上是许多较小的计数排序,而U的值很小。
另一个问题是,计数排序的重组步骤具有惊人的引用局部性-您只需在counts数组和输入数组中并行填充值即可进行扫描。如果您使用哈希表,则会失去某些局部性,因为哈希表中的元素不一定是连续存储的。
但是,这些是更多的实现参数。从根本上说,计数排序与“使用数组”无关,而与“构建频率直方图”有关。正好是在构建直方图时,常规的旧数组通常比哈希表更可取。