【问题标题】:How Bucket sort is considered under Linear Sorting?线性排序下如何考虑桶排序?
【发布时间】:2013-05-18 19:11:28
【问题描述】:

我想探索我对桶排序的分析,如下所示。
桶排序有多种实现方式。其中一些如下。
类型 1:
如果我们知道要排序的元素的范围,我们可以设置 每个可能的元素的桶,并且只是将元素扔到它们相应的桶中。然后我们 按顺序清空桶,结果是一个排序列表。 在实现这个算法时,我们可以很容易地使用一个数组来表示我们的桶,其中的值 每个数组索引将代表相应存储桶中元素的数量。如果我们有整数 在范围 [0..max] 上,然后我们设置一个 (max + 1) 整数数组并将所有值初始化为 零。然后我们依次通过未排序的数组,读取每个元素的值,然后 到桶数组中的相应索引,并增加那里的值。

时间:O(N)
空格:O(1)
类型 2:

示例:按年龄对一组人进行排序
年龄与用于排序的任意整数有些不同。因此,它的范围很小 [0-150](所有人的年龄都在 0 到 150 之间)。所以最快的排序方法是分配151个链表(我们称之为桶),并根据每个人的年龄将其数据结构放入桶中:

时间:O(N+K)
空间:O(N+K)

类型 3(类型 2 的变体,如 Wikepedia 所示)

函数 nextSort 是一个排序函数,用于对每个桶进行排序。如果使用的插入排序比最差的将是 O(n^2) 或者将使用合并排序,这样我就可以保持比 O(nlgn) 更稳定。

  • 问题:
    1>怎么算线性排序,是因为Type 1还是Type 2?
    2>如果我使用像 WIkepedia 这样的类型 3,哪种排序对每个存储桶进行有效排序?
    我知道在实践中使用插入排序的原因是我们希望存储桶很小,对于小列表,插入排序比其他任何东西都快得多。即使在实现合并排序或快速排序时,当列表变得足够小(比如低于 20 项左右)时,也会使用插入排序。
    3>对于类型 3,我可以在什么基础上决定存储桶的范围?
    这很重要,因为如果您尝试使用大量存储桶(例如远大于 n)进行存储桶排序,则运行时可能会受到支配直到扫描所有存储桶以查找您实际使用的存储桶所需的时间,即使它们中的大多数是空的。

我做了分析基于:
Wikepedia
How could the complexity of bucket sort is O(n+k)?
Design and Analysis of Algorithms Lecture notes for January 23, 1996
http://www1bpt.bridgeport.edu/~dichter/lilly/bucketsort.htm
http://cs.nyu.edu/courses/fall02/V22.0310-002/lectures/lecture-23.html
How is the complexity of bucket sort is O(n+k) if we implement buckets using linked lists?
@987654328 @

【问题讨论】:

    标签: algorithm sorting bucket-sort


    【解决方案1】:

    类型 1:
    您描述的第一种类型并不是真正的桶排序。它实际上是在计算排序或键索引计数。尽管它被认为是桶排序的变体。原因是您实际上只是在计算每个键的出现次数,而不是将键本身存储在存储桶中。

    参考:http://en.wikipedia.org/wiki/Counting_sort
    参考:http://www.cs.princeton.edu/courses/archive/spr13/cos226/demo/51DemoKeyIndexedCounting.pdf

    空格:O(1)
    我们可以为每个可能的元素设置桶,

    这不是矛盾吗?您要为每个可能的元素声明存储桶并且仍然保持 O(1)? ;)

    如果您希望算法稳定,您也不能覆盖输入数组。因此,在实践中,您需要 n + k 的空间要求:

    • 长度为“n”的输出数组(基本上与您的输入数组大小相同)
    • 'k' 个桶

    如果您检查计数排序的伪代码,您会注意到最后一个循环再次遍历输入数组以查看每个元素需要去哪里。通过按照它们在输入数组中出现的顺序执行此操作,您可以获得稳定的排序。

    PS:请记住,您不一定要对整数进行排序。如果输入是 A-Z 之间的字符数组,您也可以使用此算法。

    类型 2:

    因此,最快的排序方法是分配 151 个链表(我们称它们为桶) 并根据每个人的年龄将每个人的数据结构放入桶中:

    这可能是最简单的方法,因为您可以很容易地找到所需的存储桶,但这不一定是最快的方法。例如,另一种可能性是每 10 年创建一次存储桶。

    00 - 09
    10 - 19
    20 - 29
    ...

    当你想往桶里插入东西时,你可以这样做:

    • 对bucket(例如LinkedList)进行二进制搜索以找到正确的位置
    • 插入元素

    这样您之后也不需要对存储桶进行排序,因为所有内容都已排序。不是说这是个好主意,只是指出可能性。

    问题:

    1. 简单地说;这是一种线性排序,因为排序需要线性时间。类型 1 和类型 2 都采用 O(n + k)。另一个需要考虑的重要因素是桶排序用于对每个单独的桶进行排序的子算法。如果使用快速排序,与例如冒泡排序相比,它将导致另一个下限。也可以选择具有不同边界的非比较子算法。子算法的良好选择和桶上的分布使得桶排序不限于 O(n(log n)) 下限。 请记住,O-notation 不能保证速度,它可以保证增长率。 如果您的输入大小从“N”翻倍到“2N”,那么您的线性时间算法将比诸如气泡排序之类的 O(n^2)(更坏情况)算法更好地处理它。

    2. 插入排序对于小型数组确实很有效,这也是选择它的主要原因。再加上它稳定的事实。因为如果不使用稳定的算法对桶本身进行排序,整个算法(桶排序)就不会稳定。

    3. 很难说。在我看来,这取决于数据。如果您必须对 100 万个 32 位整数进行排序,则不会为它们创建 2^32 个存储桶。在这种情况下,最好看看其他算法(例如 LSD 基数排序),它们基本上会创建 9 个桶(每个数字 1 个)。

    【讨论】:

    • 关于 1) 的最后一条语句,即使桶排序在最坏的情况下也具有 O(n^2) 的复杂度,就像冒泡排序一样。在最好的情况下,我认为冒泡排序实际上应该击败桶排序,因为在这种情况下前者具有复杂性 (n),而后者具有复杂性 (n+k)。
    • “因为桶排序不使用元素之间的比较” - 当每个桶的子排序算法都是基于比较的时候,这句话是如何成立的?
    • @Jmoney38 点了,那里的措辞不正确。它确实取决于用于对桶进行排序的子算法。我会更新我的帖子。谢谢!
    【解决方案2】:

    当每个桶都在线性时间排序时,桶排序是线性时间的。 “Type 1”和“Type 2”都是线性时间的,因为每个桶中的所有值都成对比较相等,不需要进一步排序。

    后两个问题的答案是实际可行的。通常,您的标准库排序的作者已经确定了插入排序的适当截止值。我想桶排序的性能在很大程度上取决于相关的数据和内存子系统。

    【讨论】:

      【解决方案3】:

      您描述的Type 1和Type 2实际上是相同的意思您有一个范围。是的,在这种情况下,它是线性时间复杂度,因为每个桶内不需要进一步排序。每个存储桶都包含一种类型的值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-28
        • 2010-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-28
        • 2012-05-03
        相关资源
        最近更新 更多