我刚刚阅读了有关Bucket sort的维基百科页面。在本文中,他们说最坏的情况是O(n²)。但我认为最坏的情况复杂性是O(n + k),其中k是桶的数量。这就是我计算这种复杂性的方法:
我错过了什么吗?
答案 0 :(得分:9)
为了合并存储桶,首先需要对它们进行排序。考虑维基百科文章中给出的伪代码:
function bucketSort(array, n) is
buckets ← new array of n empty lists
for i = 0 to (length(array)-1) do
insert array[i] into buckets[msbits(array[i], k)]
for i = 0 to n - 1 do
nextSort(buckets[i])
return the concatenation of buckets[0], ..., buckets[n-1]
nextSort(buckets[i])
对每个桶进行排序。通常,使用不同的排序来对存储桶进行排序(即插入排序),因为一旦您关闭并调整大小,不同的非递归排序通常会为您提供更好的性能。
现在,考虑所有n
个元素都在同一个存储桶中的情况。如果我们使用插入排序对各个存储桶进行排序,则可能会导致O(n^2)
的最差情况。我认为答案必须取决于您选择对各个存储桶进行排序的类型。
答案 1 :(得分:1)
如果算法决定每个元素属于同一个存储桶,该怎么办?在这种情况下,每次添加元素时都需要遍历该存储桶中的链表。这需要1步,然后是2步,然后是3,4,5 ... n 。因此,时间是从1到 n 的所有数字的总和,即(n ^ 2 + n)/ 2,即O(n ^ 2)。
当然,这是“最坏情况”(一个桶中的所有元素) - 计算放置元素的桶的算法通常是为避免这种行为而设计的。
答案 2 :(得分:1)
如果你可以保证每个桶代表一个唯一值(等价项),那么最坏的情况时间复杂度就是你所指出的O(m + n)。
答案 3 :(得分:0)
Bucket sort假定输入是从均匀分布中提取的。这意味着每个桶中都有少量物品掉落。反过来,这导致O(n)的平均运行时间很好。实际上,如果在每个桶中插入n个元素,使得O(1)元素落入每个不同的桶中(每个项目插入需要O(1)),则使用插入排序对桶进行排序平均需要O(1)同样(几乎所有关于算法的教科书都证明了这一点)。由于必须对n个桶进行排序,因此平均复杂度为O(n)。
现在,假设输入不是从均匀分布中提取的。正如@mfrankli已经指出的那样,在最坏的情况下,这可能会导致所有项目都落在第一个桶中的情况。在这种情况下,插入排序将在最坏的情况下需要O(n ^ 2)。
请注意,您可以使用以下技巧来保持相同的平均O(n)复杂度,同时在最坏的情况下提供O(n log n)复杂度。在最坏的情况下,只使用具有O(n log n)复杂度的算法,而不是使用插入排序:合并排序或堆排序(但不是快速排序,只能平均达到O(n log n))。
答案 4 :(得分:0)
这是@perreal的附加答案。我试着发布它作为评论,但它太长了。当桶排序最有意义时,@ perreal正确指出。不同的答案正在对正在分类的数据做出不同的假设。例如。如果要排序的键是字符串,则可能键的范围将太大(大于桶阵列),并且我们将不得不仅使用字符串的第一个字符作为桶位置或其他策略。各个存储桶必须进行排序,因为它们包含具有不同键的项目,从而导致O(n ^ 2)。
但是如果我们在已知范围内对键是整数的数据进行排序,那么存储桶总是已经排序,因为存储桶中的键是相等的,这导致线性时间排序。不仅对存储桶进行了排序,而且排序是稳定的,因为我们可以按照添加的顺序将项目从存储桶阵列中拉出来。
我想要添加的是,如果由于要排序的键的性质而面临O(n ^ 2),则桶排序可能不是正确的方法。当您有一系列可能的键与输入的大小成比例时,您可以通过让每个桶只保持一个键的1个值来利用线性时间桶排序。