Radix排序任意长度的字符串

时间:2010-09-10 13:58:04

标签: c# algorithm sorting radix-sort

我需要对任意长度的大量文本字符串进行排序。我认为基数排序是最好的选择。列表真的很大,所以填充字符串到相同的长度是完全不可能的 是否有任何现成的实现,最好是在C#?

5 个答案:

答案 0 :(得分:2)

根据您的需要,您可能会发现将所有字符串插入某种形式的Trie是最佳解决方案。即使是基本的三元搜索工具也会比数组/字符串列表具有更小的内存占用,并且将按排序顺序存储字符串。

插入,查找和删除都是O(k * log(a)),其中a是字母表的大小(字符的可能值的数量)。由于a是常量,因此log(a)因此您最终会使用O(n * k)算法进行排序。

编辑:如果您不熟悉Tries,它们基本上是n-ary树,其中每条边代表键的单个字符。插入时,检查根节点是否包含与键的第一个字符匹配的边(或子节点)。如果是,则按照该路径重复第二个字符,依此类推。如果没有,则添加新边。在三元搜索Trie中,边/子项存储在二叉树中,因此字符按排序顺序排列,可以在log(a)时间内搜索。如果你想浪费内存,可以将边/子存储在一个大小为a的数组中,这样就可以在每一步中不断查找。

答案 1 :(得分:1)

查看此主题。 radix sort 或此radix sort implementation

答案 2 :(得分:0)

有多少,一百万?

内置List<string>.Sort()平均需要O(n * log(n))。

log2(10 ^ 6)〜= 20,对于10 ^ 6个元素,它不比O(n)慢得多。如果你的字符串长度超过20个字符,则基数排序O(n * k)将“慢”。

我怀疑基数排序会比内置排序快得多。但衡量和比较会很有趣。

答案 3 :(得分:0)

编辑:我之前提到的这些陈述有一点意义,但总的来说这是错误的。

基数排序是在大量字符串上使用的错误排序。对于像

这样的事情
I really like squirrels. Yay, yay, yay!
I really like blue jays. Yay, yay, yay!
I really like registers. Yay, yay, yay!

你会有一堆条目落在同一个桶中。您可以通过散列来避免这种情况,但是使用散列代码的用途是什么?

使用quicksort或mergesort等。 (Quicksort通常表现更好,占用内存更少,但很多例子都有O(N ^ 2)的最坏情况性能,这在实践中几乎不会发生; Mergesort表现不太好,但通常实现稳定,而且它是容易在内存中分离,在磁盘上分开。)也就是说,使用内置的排序功能。


编辑:嗯,事实证明,至少在开头有很长重复的非常大的文件(例如源代码)和许多行完全相同(实际上是100倍重复),基数排序确实开始变得具有竞争力快速排序。我很惊讶!但是,无论如何,这是我用来实现基数排序的代码。它是在Scala中,而不是C#中,但我是以相当迭代的方式编写的,所以它应该是合理明显的。只有两个棘手的位是(a(i)(ch) & 0xFF)是从字节数组中提取0-255字节(字节是有符号的),counts.scanLeft(0)(_ + _)形成一个累计的计数总和,从零开始(然后indices.clone.take(257)获取除最后一个之外的所有内容),并且Scala允许多个参数列表(因此我从具有递归中使用的默认值的参数中拆分始终提供的参数)。这是:

def radixSort(a: Array[Array[Byte]])(i0: Int = 0, i1: Int = a.length, ch: Int = 0) {
  val counts = new Array[Int](257)
  var i = i0
  while (i < i1) {
    if (a(i).length <= ch) counts(0) += 1
    else { counts((a(i)(ch)&0xFF)+1) += 1 }
    i += 1
  }
  val indices = counts.scanLeft(0)(_ + _)
  val starts = indices.clone.take(257)
  i = i0
  while (i < i1) {
    val bucket = if (a(i).length <= ch) 0 else (a(i)(ch)&0xFF)+1
    if (starts(bucket)+i0 <= i && i < starts(bucket)+i0+counts(bucket)) {
      if (indices(bucket) <= i) indices(bucket) = i+1
      i += 1
    }
    else {
      val temp = a(indices(bucket)+i0)
      a(indices(bucket)+i0) = a(i)
      a(i) = temp
      indices(bucket) += 1
    }
  }
  i = 1
  while (i < counts.length) {
    if (counts(i)>1) {
      radixSort(a)(i0+starts(i),i0+starts(i)+counts(i),ch+1)
    }
    i += 1
  }
}

时间是7M行源代码(100x重复70k行),基数排序与内置库排序相关联,之后获胜。

答案 4 :(得分:-2)

String.Compare()重载正在使用这种字符串比较。看看你需要的是把它喂给你 排序算法。

<强>更新

这是实施:

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int nativeCompareString(int lcid, string string1, int offset1, int length1, string string2, int offset2, int length2, int flags);

使用您自己的实现很难击败这个本机非托管实现。

相关问题