C#中使用基數(shù)排序算法對字符串進行排序的示例
開始之前
假設最長字符串的長度是L,以L作為輸入的長度, 然后假定所有的字符串都"補齊"到此長度,這個補齊只是邏輯上的,我們可以假想有一種"空字符", 它小于任何其它字符,用此字符補齊所有長度不足的字符串。例如:最長的字符串長度為9,有一個字符串A長度為6, 那么當比較第7位字符的時候,我們讓A[7]為"空字符"。
如果要包含所有的字符似乎并不容易,我們先定義一個字符集, 待排序字符串中的所有字符都包含在這個字符集里
//字符集 private string _myCharSet = "0123456789qwertyuiopasdfghjklzxcvbnm";
再來一個生成隨機字符串的方法(C#實現(xiàn)):
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;
}
這里按照字符的整數(shù)表示來確定桶的范圍,再為"空字符"準備一個桶。 為了表示"空字符"這個特例,這里用default(char),即'\0'表示它, 因為當調(diào)用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;
}
}
//確定字符的整數(shù)范圍
int rangeStart = _myCharSet[0];
int rangeEnd = _myCharSet[0];
foreach (char ch in _myCharSet)
{
if (ch < rangeStart)
rangeStart = ch;
if (ch >= rangeEnd)
rangeEnd = ch + 1;
}
//也要為"空字符"分配一個桶,其索引為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))
{ //"空字符"的處理
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--;
}
}
稍作"改良"
用作確定字符的整數(shù)范圍的代碼略顯蛋疼,而且根據(jù)字符集來看, 并不是區(qū)間內(nèi)所有的整數(shù)對應的字符都可能出現(xiàn),因此會有這樣的情況: 我們給某些根本不會出現(xiàn)的字符分配了桶,這純屬浪費。 我們可以用一個字典(散列)來記錄字符和它的桶之間的映射。于是有了下面的代碼。
private Dictionary<char, int> _charOrderDict =
new Dictionary<char, int>(_myCharSet.Length);
void BuildCharOrderDict()
{
char[] sortedCharSet = _myCharSet.ToArray();
//使用默認的比較器排序
Array.Sort(sortedCharSet);
//為"空字符"單獨創(chuàng)建映射
_charOrderDict.Add(default(char), 0);
for (int i = 0; i < sortedCharSet.Length; i++)
{
// 保存的是字符及其對應的桶的索引
_charOrderDict.Add(sortedCharSet[i], i + 1);
}
}
也可以不用默認的字符排序來作為映射,而完全自己定義字符之間的大小關(guān)系。 下面是調(diào)整后的代碼:
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;
}
}
//為每一個字符(包括空字符'\0')分配一個桶
//"空字符"索引應為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);
//根據(jù)字符順序的定義查詢字符
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)。表面上看,基數(shù)排序更好,不過嚴格來說, 基數(shù)排序的時間復雜度應該是O(k∗n)O(k∗n),其中k和字符串長度正相關(guān)。 此時兩種算法的比較可以通過比較k和lognlogn的比較結(jié)果近似得出。 如果字符串的長度很長,即k很大,而輸入規(guī)模n不大的時候, 就會有k>lognlogn,此時快速排序反而更有優(yōu)勢。反之,則基數(shù)排序可能更優(yōu)。
最后...
杯具的是,當我擴大字符集,將鍵盤上所有字符都加進去后, 發(fā)現(xiàn)基數(shù)排序的結(jié)果和Array.Sort(string[]方法的排序結(jié)果并不一樣。 仔細觀察資源管理器對文件名的排序,才發(fā)現(xiàn)其字符串排序的規(guī)則要復雜的多,并非簡單的比較字符。 查詢相關(guān)資料后發(fā)現(xiàn),字符串的排序甚至還要考慮區(qū)域文化的影響,即使都是拉丁字母, 不同地區(qū)的排序規(guī)則都可能不一樣,因此, 使用基數(shù)排序?qū)崿F(xiàn)的字符串排序算法好像并無多大實用價值<T-T>。
欄 目:C#教程
下一篇:automation服務器不能創(chuàng)建對象 解決方法
本文標題:C#中使用基數(shù)排序算法對字符串進行排序的示例
本文地址:http://m.jygsgssxh.com/a1/C_jiaocheng/6464.html
您可能感興趣的文章
- 01-10C#通過反射獲取當前工程中所有窗體并打開的方法
- 01-10C#實現(xiàn)Winform中打開網(wǎng)頁頁面的方法
- 01-10C#實現(xiàn)由四周向中心縮小的窗體退出特效
- 01-10Extjs4如何處理后臺json數(shù)據(jù)中日期和時間
- 01-10C#使用Dispose模式實現(xiàn)手動對資源的釋放
- 01-10C#3.0使用EventLog類寫Windows事件日志的方法
- 01-10C#中DataGridView常用操作實例小結(jié)
- 01-10C#編程獲取資源文件中圖片的方法
- 01-10C#使用windows服務開啟應用程序的方法
- 01-10asp.net中XML如何做增刪改查操作


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當前操作系統(tǒng)已
隨機閱讀
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10C#中split用法實例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢data目錄下的sessions文件夾有什


