算法學習筆記(八)——字符串排序


字符串排序

1.低位優先的字符串排序(LSD)

低位優先的字符串排序是從右到左依次檢查鍵中的字符,這種方法適用於鍵的長度都相同的字符串。

假設字符串的長度都為w,那么就從右向左按基數排序的方法排序w遍。

代碼實現:

//低位優先排序
template <typename T>
void LSD(vector<T> &a, int w) //通過前w個字符對a排序
{
    int N = a.size(); //N個字符串
    int R = 256;      //R個字符構成的字母表
    vector<T> aux(N); //拷貝數組

    for (int d = w - 1; d >= 0; d--)
    {
        vector<int> count(R + 1);
        for (int i = 0; i < N; i++) //計算出現頻率
            count[a[i][d] + 1]++;
        for (int r = 0; r < R; r++) //將頻率轉換為索引
            count[r + 1] += count[r];
        for (int i = 0; i < N; i++) //將元素分類
            aux[count[a[i][d]]++] = a[i];
        for (int i = 0; i < N; i++) //回寫
            a[i] = aux[i];
    }
}

測試數據:

4PGC938
2IYE230
3CIO720
1ICK750
1OHV845
4JZY524
1ICK750
3CIO720
1OHV845
1OHV845
2RLA629
2RLA629
3ATW723

測試結果:

1ICK750
1ICK750
1OHV845
1OHV845
1OHV845
2IYE230
2RLA629
2RLA629
3ATW723
3CIO720
3CIO720
4JZY524
4PGC938

性能分析:
對於基於R個字符的字母表的N個長為W的字符串為鍵的元素,LSD需要訪問~7WN+3WR次數組,使用的額外空間與N+R成正比。對於常用的應用,R會遠小於N,因此LSD的總運行時間與WN成正比。

2.高位優先的字符串排序(MSD)

高位優先的字符串排序會從左到右,首先查看的是最高位的字符,將最高位的字符按照順序排列之后,再將每個首字母對應的子數組排序。MSD是一種通用的字符串排序。

在MSD的實現中,我們會用插入排序對排序數組較小的時候進行優化,這樣可以大大提高MSD的效率。

代碼實現:

//高位優先排序
class MSD
{
private:
    int R = 256;        //基數
    int M = 3;          //小數組的切換閾值
    vector<string> aux; //輔助數組
    int charAt(string s, int d)
    {
        return (d < s.size()) ? s[d] : -1;
    }

public:
    MSD(vector<string> &a)
    {
        int N = a.size();
        aux.resize(N);
        sort(a, 0, N - 1, 0);
    }

    void sort(vector<string> &a, int lo, int hi, int d)
    {
        if (hi <= lo + M)
        {
            for (int i = lo; i <= hi; i++)	//插入排序
            {
                string key = a[i];
                int j = i - 1;
                while (j >= 0 && key < a[j])
                {
                    a[j + 1] = a[j];
                    j--;
                }
                a[j + 1] = key;
            }
            return;
        }

        vector<int> count(R + 2);
        for (int i = lo; i <= hi; i++)		//計算頻率
            count[charAt(a[i], d) + 2]++;
        for (int r = 0; r < R + 1; r++)		//將頻率轉換為索引
            count[r + 1] += count[r];
        for (int i = lo; i <= hi; i++)		//數據分類
            aux[count[charAt(a[i], d) + 1]++] = a[i];
        for (int i = lo; i <= hi; i++)		//回寫
            a[i] = aux[i - lo];

        //遞歸的以每個字符為鍵進行排序
        for (int r = 0; r < R; r++)
            sort(a, lo + count[r], lo + count[r + 1] - 1, d + 1);
    }
};

測試數據:

she
sells
seashells
by
the
seashore
the
shells
she
sells
are
surely
seashells

測試結果:

are
by
seashells
seashells
seashore
sells
sells
she
she
shells
surely
the
the

性能分析:

對於大小為R的字母表的N個字符串排序,高位優先的字符串排序算法訪問次數在8N+3R到~7wN+3wR,w為字符串的平均長度。所需的空間最壞情況下與RW成正比,W為最長字符串的長度。

3.三向字符串快速排序

三向字符串快速排序通過優化MSD,根據鍵的首字母進行三向切分(大於,等於和小於鍵),僅在中間子數組的下一個字符繼續遞歸排序。

代碼實現:

//三向字符串快速排序
class Quick3Srting
{
private:
    int charAt(string s, int d)
    {
        return (d < s.size()) ? s[d] : -1;
    }

public:
    Quick3Srting(vector<string> &a)
    {
        sort(a, 0, a.size() - 1, 0);
    }

    void sort(vector<string> &a, int lo, int hi, int d)
    {
        if (hi <= lo)
            return;
        int lt = lo, gt = hi;
        int v = charAt(a[lo], d);
        int i = lo + 1;
        while (i <= gt)
        {
            int t = charAt(a[i], d);
            if (t < v)
                swap(a[lt++], a[i++]);
            else if (t > v)
                swap(a[i], a[gt--]);
            else
                i++;
        }
        sort(a, lo, lt - 1, d);
        if (v >= 0)
            sort(a, lt, gt, d + 1);
        sort(a, gt + 1, hi, d);
    }
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM