C#中使用基數排序演算法對字串進行排序的樣本_C#教程

來源:互聯網
上載者:User

開始之前

假設最長字串的長度是L,以L作為輸入的長度, 然後假定所有的字串都"補齊"到此長度,這個補齊只是邏輯上的,我們可以假想有一種"Null 字元", 它小於任何其它字元,用此字元補齊所有長度不足的字串。例如:最長的字串長度為9,有一個字串A長度為6, 那麼當比較第7位字元的時候,我們讓A[7]為"Null 字元"。

如果要包含所有的字元似乎並不容易,我們先定義一個字元集, 待排序字串中的所有字元都包含在這個字元集裡

//字元集private string _myCharSet = "0123456789qwertyuiopasdfghjklzxcvbnm";

再來一個產生隨機字串的方法(C#實現):

private Random _random = new Random(); string[] GetRandStrings(int size, int minLength, int maxLength){  string[] strs = new string[size];  int len = 0;  StringBuilder sb = new StringBuilder(maxLength);   for (int i = 0; i < strs.Length; i++)  {    //先隨機確定一個長度    len = _random.Next(minLength, maxLength);    for (int j = 0; j < len; j++)    {      //隨機選取一個字元      sb.Append(_myCharSet[_random.Next(_myCharSet.Length)]);    }    strs[i] = sb.ToString();    sb.Clear();  }  return strs;}

這裡按照字元的整數表示來確定桶的範圍,再為"Null 字元"準備一個桶。 為了表示"Null 字元"這個特例,這裡用default(char),即'\0'表示它, 因為當調用string.ElementAtOrDefault(int)方法時,如果超出索引會返回'\0'。

初級版本(C#)

void StringRadixSort(string[] strArray){  if (strArray == null    || strArray.Length == 0    || strArray.Contains(null))  {    return;  }   //獲得字串的最大長度  int maxLength = 0;  foreach (string s in strArray)  {    if (s.Length > maxLength)    {      maxLength = s.Length;    }  }   //確定字元的整數範圍  int rangeStart = _myCharSet[0];  int rangeEnd = _myCharSet[0];  foreach (char ch in _myCharSet)  {    if (ch < rangeStart)      rangeStart = ch;    if (ch >= rangeEnd)      rangeEnd = ch + 1;  }   //也要為"Null 字元"分配一個桶,其索引為0  int bucketCount = rangeEnd - rangeStart + 1;  LinkedList<string>[] buckets = new LinkedList<string>[bucketCount];   //初始化所有的桶  for (int i = 0; i < buckets.Length; i++)  {    buckets[i] = new LinkedList<string>();  }   //從最後一個字元開始排序  int currentIndex = maxLength - 1;  while (currentIndex >= 0)  {    foreach (string theString in strArray)    {      //如果超出索引,返回'\0'字元(default(char))      char ch = theString.ElementAtOrDefault(currentIndex);      if (ch == default(char))      {  //"Null 字元"的處理        buckets[0].AddLast(theString);      }      else      {  //將字元對應表到桶        int index = ch - rangeStart + 1;        buckets[index].AddLast(theString);      }    }    //從桶裡依次取回字串,完成一趟排序    int i = 0;    foreach (LinkedList<string> bucket in buckets)    {      while (bucket.Count > 0)      {        strArray[i++] = bucket.First();        bucket.RemoveFirst();      }    }    currentIndex--;  }}

稍作"改良"

用作確定字元的整數範圍的代碼略顯蛋疼,而且根據字元集來看, 並不是區間內所有的整數對應的字元都可能出現,因此會有這樣的情況: 我們給某些根本不會出現的字元分配了桶,這純屬浪費。 我們可以用一個字典(散列)來記錄字元和它的桶之間的映射。於是有了下面的代碼。

private Dictionary<char, int> _charOrderDict =         new Dictionary<char, int>(_myCharSet.Length);void BuildCharOrderDict(){  char[] sortedCharSet = _myCharSet.ToArray();  //使用預設的比較子排序  Array.Sort(sortedCharSet);  //為"Null 字元"單獨建立映射  _charOrderDict.Add(default(char), 0);  for (int i = 0; i < sortedCharSet.Length; i++)  {    // 儲存的是字元及其對應的桶的索引    _charOrderDict.Add(sortedCharSet[i], i + 1);  }}

也可以不用預設的字元排序來作為映射,而完全自己定義字元之間的大小關係。 下面是調整後的代碼:

void StringRadixSort(string[] strArray){  if (strArray == null    || strArray.Length == 0    || strArray.Contains(null))  {    return;  }  //獲得字串的最大長度  int maxLength = 0;  foreach (string s in strArray)  {    if (s.Length > maxLength)    {      maxLength = s.Length;    }  }   //為每一個字元(包括Null 字元'\0')分配一個桶  //"Null 字元"索引應為0  int bucketCount = _myCharSet.Length + 1;  LinkedList<string>[] buckets = new LinkedList<string>[bucketCount];   //初始化所有的桶  for (int i = 0; i < buckets.Length; i++)  {    buckets[i] = new LinkedList<string>();  }   //從最後一個字元開始排序  int currentIndex = maxLength - 1;  while (currentIndex >= 0)  {    foreach (string theString in strArray)    {      //如果超出索引,返回'\0'字元(default(char))      char ch = theString.ElementAtOrDefault(currentIndex);      //根據字元順序的定義查詢字元      int index = _charOrderDict[ch];      buckets[index].AddLast(theString);    }    //從桶裡依次取回字串,完成一趟排序    int i = 0;    foreach (LinkedList<string> bucket in buckets)    {      while (bucket.Count > 0)      {        strArray[i++] = bucket.First();        bucket.RemoveFirst();      }    }    currentIndex--;  }}

Now, it works! 如果採用的快速排序來做, 其時間複雜度為O(n∗logn)O(n∗logn)。表面上看,基數排序更好,不過嚴格來說, 基數排序的時間複雜度應該是O(k∗n)O(k∗n),其中k和字串長度正相關。 此時兩種演算法的比較可以通過比較k和lognlogn的比較結果近似得出。 如果字串的長度很長,即k很大,而輸入規模n不大的時候, 就會有k>lognlogn,此時快速排序反而更有優勢。反之,則基數排序可能更優。

最後...

杯具的是,當我擴大字元集,將鍵盤上所有字元都加進去後, 發現基數排序的結果和Array.Sort(string[]方法的排序結果並不一樣。 仔細觀察資源管理員對檔案名稱的排序,才發現其字串排序的規則要複雜的多,並非簡單的比較字元。 查詢相關資料後發現,字串的排序甚至還要考慮地區文化的影響,即使都是拉丁字母, 不同地區的定序都可能不一樣,因此, 使用基數排序實現的字串排序演算法好像並無多大實用價值<T-T>。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.