線性時間排序算法:計數、桶、基數排序

更多排序算法參見:“10大排序算法” 總結

計數排序

  • 針對數據【範圍相差不大的整數】的排序。
  • 找到數組a最小值Min與最大值Max,相差範圍爲m。
  • 將數組a的值映射到[0,m-1]範圍內,作爲數組num的下標,則num[i]表示數組a中數值爲i+Min的個數,這樣排序後的數組可以通過遍歷一遍num數組獲得。
  • 如果要實現穩定性,我們可以對num數組求前綴和得到sum數組,則sum[i]表示a中數值爲i+Min的數排到了第sum[i]個。這樣倒序遍歷一遍a,則sum[a[i]]爲穩定排序後a[i]的位置(注意這裏的位置是從1開始計數!),--sum[a[i]]。
  • 可看出計數排序是穩定排序,時間複雜度爲o(m+n),空間複雜度爲o(m)

 

#include<iostream>
using namespace std;
const int inf=0x3f3f3f3f;

void CountSort(int a[], int n)
{
    int Min=inf,Max=-1;
    for(int i=0;i<n;++i)
    {
        if(a[i]>Max)
            Max=a[i];
        if(a[i]<Min)
            Min=a[i];
    }
    int m=Max-Min+1;
    int *num = new int[m]();
    for(int i=0;i<n;++i)
        num[a[i]-Min]++;
    for(int i=1;i<m;++i)
        num[i]+=num[i-1];
    int *sortedArr = new int[n];
    for(int i=n-1;i>=0;--i)
    {
        sortedArr[num[a[i]-Min]-1]=a[i];
        num[a[i]-Min]--;
    }
    for(int i=0;i<n;++i)
        a[i]=sortedArr[i];
    delete sortedArr;
    delete num;
}

int main()
{
    int a[100],i,n;
    while(cin>>n)
    {
        for(i=0; i<n; i++)
            cin>>a[i];
        CountSort(a,n);
        for(i=0; i<n; i++)
            cout<<a[i]<<" ";
        cout<<endl;
    }
    return 0;
}

桶排序

  • 針對計數排序無法處理【浮點型數據】設計
  • 建立m個桶,每個桶代表一個浮點範圍。則遍歷一遍數組,將數組元素放入對應範圍的桶,然後對每個桶的元素分別拍排序,可以採用歸併排序等,最後按序依次輸出每個桶的元素即可。
  • 怎麼確定m及桶的範圍呢?我們可以另m=n,即建立n個桶,除了最後一桶範圍是[Max,Max],前面各桶的區間按比例確定:區間跨度 = (最大值-最小值)/ (桶的數量 - 1)

 

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;

void BucketSort(double a[], int n)
{
    if(1==n)
        return;
    double Max=a[0],Min=a[0];
    for(int i=1;i<n;++i)
    {
        if(a[i]>Max)
            Max=a[i];
        if(a[i]<Min)
            Min=a[i];
    }
    double interval=(Max-Min)/(n-1);
    vector<double>v[n];
    for(int i=0;i<n;++i)
    {
        int idx=(a[i]-Min)/interval;
        v[idx].push_back(a[i]);
    }
    for(int i=0;i<n;++i)
        sort(v[i].begin(),v[i].end());
    int cnt=0;
    for(int i=0;i<n;++i)
    {
        int sz=v[i].size();
        for(int j=0;j<sz;++j)
            a[cnt++]=v[i][j];
    }
}

int main()
{
    int i,n;
    double a[100];
    while(cin>>n)
    {
        for(i=0; i<n; i++)
            cin>>a[i];
        BucketSort(a,n);
        for(i=0; i<n; i++)
            cout<<a[i]<<" ";
        cout<<endl;
    }
    return 0;
}
/*
5 4.5 0.5 2.18 0.84 3.25
*/

基數排序

  • 針對計數排序不能對【字符串、範圍相差很大的數據】排序的缺陷。如11位的電話號碼排序,英文名排序等。
  • 對排序元素的每一位分別進行計數排序。
  • 時間複雜度 O(k(n*m)) , k爲元素的最大位數
  • 如果元素的位數不一樣,可適當補0。
  • 字符串的排序本應從最高位開始比,所以我們應倒着計數排序,因爲後一輪會改變前一輪的排序結果。即LSD(低位優先排序)

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;

const int ASCII_RANGE=128;

void radixSort(string a[], int n ,int max_len)
{
    string *sortedArr = new string[n];
    for(int k=max_len;k>=0;--k)
    {
        int *num = new int[ASCII_RANGE]();
        for(int i=0;i<n;++i)
        {
            if(a[i].size()<=k)
                num[0]++;
            else
                num[a[i][k]]++;
        }
        for(int i=1;i<ASCII_RANGE;++i)
            num[i]+=num[i-1];

        for(int i=n-1;i>=0;--i)
        {
            int cur;
            if(a[i].size()<=k)
                cur=0;
            else
                cur=a[i][k];
            sortedArr[num[cur]-1]=a[i];
            cout<<num[cur]-1<<" "<<a[i]<<endl;
            num[cur]--;
        }
        delete num;
        for(int i=0;i<n;++i)
            a[i]=sortedArr[i],cout<<a[i]<<" ";
        cout<<endl;
    }
    delete sortedArr;
}

int main()
{
    int i,n;
    string a[100];
    while(cin>>n)
    {
        int max_len=0;
        for(i=0; i<n; i++)
        {
            cin>>a[i];
            int sz=a[i].size();
            max_len=max(max_len,sz);
        }
        radixSort(a,n,max_len);
        for(i=0; i<n; i++)
            cout<<a[i]<<endl;
    }
    return 0;
}
/*
8
qd ab qwe hhh a cws hhd hhz
*/

 


圖片來自:程序員小灰

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章