在线性时间内对[log n]不同的值进行排序

时间:2015-04-12 12:23:51

标签: algorithm sorting radix-sort

我有一个n个整数数组,只能假设 log n 可能的值(以及任何值)。例如,在S = [349,12,12,283,349,283,283,12]中,只有3个不同的数字(log 8 = 3)

我必须在不到O(nlogn)的时间内对此数组进行排序。我应该使用哪种算法?也许Radix排序计数排序?它的分析怎么样?

3 个答案:

答案 0 :(得分:3)

由于给定的是只有log(n)个唯一元素,您可以使用以下算法在O(n)时间内获取排序列表:

  1. 创建列表中有多少个不同项目的映射,并保留每个项目的计数(基本上是键的字典/哈希映射,计数)
    • 这是对输入列表的单次迭代,因此O(n)步骤。
  2. 根据键的大小log(n)排序上面的元组列表。
    • 假设我们使用合并排序,那么合并排序的时间复杂度为k*log(k),其中k是输入的大小。
    • k替换为log(n),我们将此步骤的复杂性视为O(log(n)*log(log(n)))
    • 因为在复杂性方面,O(log(n)*log(log(n)))< O(n),因此直到此步骤的总体复杂度为O(n) + O(log(n)*log(log(n))),这相当于O(n)
  3. 迭代排序的键,并使用单个for循环生成排序列表,其中每个值按其计数重复。这里将进行最多O(n)次迭代。
  4. 总的来说,上面的算法将在O(n)时间运行。

答案 1 :(得分:2)

  1. 计算列表中每个元素的数量(使用哈希表)(时间:O(n))。
  2. 重复删除列表(时间:O(n))。
  3. 对现在被删除的项目进行排序(时间:O(log n * log log n))。
  4. 使用每个项目的正确份数(时间:O(n))构建一个列表。
  5. 总的来说,这是O(n)并且易于实现。

    这里有一些python实现了这个:

    def duplicates_sort(xs):
        keys = collections.Counter(xs)
        result = []
        for k in sorted(keys):
            result.extend([k] * keys[k])
        return result
    

答案 2 :(得分:1)

基数排序复杂度为O(dn),其中d为数字中的位数。

算法仅在d为常数时才以线性时间运行!在您的情况下,d = 3log(n),您的算法将在O(nlog(n))中运行。

老实说,我不确定如何在线性时间内解决这个问题。是否有关于数字性质的其他信息,我想知道是否有任何其他信息缺失关于数字的性质......

嗯,这是DNA的MSD基数排序的简单实现。它是用D语言编写的,因为这是我最常使用的语言,因此最不可能犯下愚蠢的错误,但它很容易被翻译成其他语言。它就位,但要求2 * seq.length通过阵列。

void radixSort(string[] seqs, size_t base = 0) {
    if(seqs.length == 0)
        return;

    size_t TPos = seqs.length, APos = 0;
    size_t i = 0;
    while(i < TPos) {
        if(seqs[i][base] == 'A') {
             swap(seqs[i], seqs[APos++]);
             i++;
        }
        else if(seqs[i][base] == 'T') {
            swap(seqs[i], seqs[--TPos]);
        } else i++;
    }

    i = APos;
    size_t CPos = APos;
    while(i < TPos) {
        if(seqs[i][base] == 'C') {
            swap(seqs[i], seqs[CPos++]);
        }
        i++;
    }
    if(base < seqs[0].length - 1) {
        radixSort(seqs[0..APos], base + 1);
        radixSort(seqs[APos..CPos], base + 1);
        radixSort(seqs[CPos..TPos], base + 1);
        radixSort(seqs[TPos..seqs.length], base + 1);
   }
}

显然,这是一种特定的DNA,而不是一般的,但它应该很快。

编辑:我很好奇这段代码是否真的有效,所以我在等待自己的生物信息学代码运行的同时测试/调试了它。现在上面的版本实际上已经过测试和运行。对于每个5个碱基的1000万个序列,它比优化的 introsort 快约3倍。

让我们看两个十进制数的例子:

49, 25, 19, 27, 87, 67, 22, 90, 47, 91

按第一位数排序

19, 25, 27, 22, 49, 47, 67, 87, 90, 91

接下来,按第二个数字排序,产生

90, 91, 22, 25, 27, 47, 67, 87, 19, 49
似乎错了,不是吗?或者这不是你在做什么?如果我弄错了,也许你可以告诉我们代码。

如果您对具有相同第一个数字的所有组进行第二次排序,则您的算法将等同于递归版本。它也会稳定。唯一的区别是你要对广告优先级进行排序,而不是深度优先。

更新

检查此答案:sort O(nlogn)