數據結構中的常見排序

1、基數排序ios

基數排序的思想比較好理解,便是從各位數開始比較起,一直比較到最高位位置,每次比較都是在前一次比較的基礎上進行的。算法

代碼以下:數組

/*
    基數排序,即爲按照每位數來將其分類,而後在前一次分類的順序基礎上,再次進行分類,直到全部分類標準都執行完畢
    時間複雜度爲n 
*/
#include <iostream>
using namespace std;
int GetNumByPos(int num,int dight){
    int value=-1;
    while(dight>0){
        value=num%10;
        num=num/10;
        dight--;
    }
    return value;
}
void RadixSort(int * s,int len,int dight){
    int * buttet=new int[len];
    int count[dight];
    for(int i=1;i<=dight;i++){
        for(int j=0;j<dight;j++){
            count[j]=0;
        }
        for(int j=0;j<len;j++){
            if(GetNumByPos(s[j],i)>=0){
                count[GetNumByPos(s[j],i)]++;
            }
        }
        for(int j=1;j<dight;j++){
            count[j]=count[j]+count[j-1];
        }
        for(int j=len-1;j>=0;j--){
            buttet[count[GetNumByPos(s[j],i)]-1]=s[j];
            count[GetNumByPos(s[j],i)]--;
        }
        for(int j=0;j<len;j++){
            s[j]=buttet[j];
        }
    } 
}
int main(){
    int list[10]={278,109,63,930,589,184,505,269,8 ,83};
    RadixSort(list,10,10);
    for(int i=0;i<10;i++){
        cout<<list[i]<<" ";
    }
    cout<<endl;
    return 0;
}

2、二路歸併排序ui

二路歸併排序的思想是開始就將數列劃分爲兩個部分,而後依次遞歸的對這兩部分執行二分操做,直到全部的部分都只包含一個元素位置,此時,再分別對這些部分執行歸併操做。直到整個數列都被歸併完畢。spa

代碼以下:code

/*
    二路歸併排序,採用了遞歸的作法,它首先將整個隊列劃分爲兩個部分,再一次對這兩個部分進行二分操做,
    直到每一個部分只包含一個元素爲止,而後再依次對這些只包含一個元素的部分開始進行歸併,而後遞歸操做會依次向上
    進行回溯,最後返回已排好序的隊列,排序完成。
    時間複雜度n*log2n  
*/
#include <iostream>
using namespace std;
void Merge(int array[], int p, int q, int r)
{
    int n1 = q - p + 1;
    int n2 = r - q;

    int *L;
    L = (int*)malloc(sizeof(int)*n1);
    int *R;
    R = (int*)malloc(sizeof(int)*n2);

    int i = 0;
    int j = 0;

    for(i; i < n1; i++)
        L[i] = array[i + p];
    for(j; j < n2; j++)
        R[j] = array[j + q  +1];

    i = j = 0;

    int k = p;

    while(i!=n1 && j!= n2)
    {
        if(L[i] <= R[j])
            array[k++] = L[i++];
        else
            array[k++] = R[j++];
    }

    while(i < n1)
        array[k++] = L[i++];
    while(j < n2)
        array[k++] = R[j++];

    free(L);
    free(R);
}

void merge(int * b,int s,int mid,int t){
    int a[t-s+1];
    int k=0;for(int i=s;i<=t;i++){
        a[k++]=b[i];
    }int i,j;
    int id=s;
    for(i=s,j=mid+1;i<=mid&&j<=t;){if(a[i-s]<a[j-s]){
            b[id++]=a[i-s];
            i++;    
        }else{
            b[id++]=a[j-s];
            j++;
        }
    }while(i<=mid){
        b[id++]=a[i-s];
        i++;
    }
    while(j<=t){
        b[id++]=a[j-s];
        j++;
    }
}
void TwoGuiBing(int * a,int s,int e){
    int * bb;
    if(s<e){
        int mid=(s+e)/2;
        TwoGuiBing(a,s,mid);
        TwoGuiBing(a,mid+1,e);
        cout<<"before sort: ";
        for(int i=s;i<=e;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
        merge(a,s,mid,e);
        cout<<"after sort: ";
        for(int i=s;i<=e;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
}
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};//1 1 2 3 4 4 5 6 65 98 
    TwoGuiBing(s,0,9);
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

3、快讀排序blog

快速排序的思想是每次都選擇一個標誌,將整個數列劃分爲兩個部分,而後在依次遞歸的對這兩個部分通用選取標誌進行劃分。直到每一個部分大小變爲1爲止。排序

代碼以下:遞歸

/*
    快速排序就是首先將s[0]選定爲咱們的劃分標誌,而後,將隊列劃分爲兩個部分。而後再分別對這兩個部分進行劃分。
    直到每一個部分只包含一個元素爲止。
    時間複雜度n*log2n  不穩定排序,數列有序則退化爲冒泡排序。 
*/ 
#include <iostream>
using namespace std;
void swap(int & a,int & b){
    int temp=a;
    a=b;
    b=temp;
}
int func(int * s,int b,int e){
    int flag=s[b];
    while(b<e){
        while(b<e&&s[e]>=flag) e--;//必定要>=不然若是最後兩個數與flag相同,那麼整個while就會陷入死循環中。 
        swap(s[b],s[e]);
        while(b<e&&s[b]<=flag) b++;
        swap(s[b],s[e]);
    }
    return b;
}
void quickSort(int * s,int b,int e){
    if(b<e){
        int pos=func(s,b,e);
        quickSort(s,b,pos-1);
        quickSort(s,pos+1,e);
    }
}
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};//1 1 2 3 4 4 5 6 65 98 
    quickSort(s,0,9);
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

4、堆排序隊列

堆排序,經過每次構造一個大頂堆或者小頂堆,來查找出當前數列的最大值或最小值,而後交換堆頂與最後一個葉子節點,同時,將排序的範圍減一,直到排序的範圍減少到1,記住,初始時須要先將數列構造爲大頂堆或小頂堆。

代碼以下:

/*
    時間複雜度n*log2n,咱們直到堆排序,須要首先構造一個大頂堆或者小頂堆,而後開始選出最大值或最小值,在這一過程當中,將其移動
    到堆頂,而後交換堆頂和堆尾,縮小堆的大小,再次排序,直到堆的大小爲1爲止。 
*/
#include <iostream>
using namespace std;
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};//1 1 2 3 4 4 5 6 65 98 
    for(int i=10;i>0;i--){//之因此將i設置爲從10開始,就是考慮了初始數列構造大頂堆或小頂堆的問題。 if(i<10){//初始時,無需交換堆頂和最後一個葉子結點。 int temp=s[i];
            s[i]=s[0];
            s[0]=temp;    
        }
        for(int j=(i)/2;j>=0;j--){
            int faV=s[j];
            int fa=j;
            int child=2*j+1;
            while(child<i){
                if(child+1<i&&s[child]<s[child+1]){
                    child++;
                }
                if(s[child]>s[fa]){//一直順着查找下去。
                    s[fa]=s[child];
                    fa=child;
                    child=2*fa+1;
                }else{
                    break;
                }
                s[fa]=faV;
            }
        }
    }
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
} 

5、希爾排序

希爾排序經過設置間隔,來將數列進行分組,而後對於每組的數據使用直接插入排序。以後,在將間隔減少爲其的一半,直到間隔減少到0爲止。

代碼以下:

/*
    經過設置間隔來實現,首先將間隔設置爲隊列大小的一半,對相同間隔內的元素執行直接插入排序。
    時間複雜度n到n*n 
*/
#include <iostream>
using namespace std;
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};
    int jianGe=10/2;
    while(jianGe>=1){
        for(int i=jianGe;i<10;i++){
            int j=i-jianGe;
            int val=s[i];//在後面的變換中,s[i]對應的值已經發生了變化。 
            while(s[j]>val&&j>=0){
                s[j+jianGe]=s[j];
                j=j-jianGe;
            }
            s[j+jianGe]=val;
        }
        jianGe=jianGe/2; 
    }
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

6、直接插入排序

直接插入排序的思想就是假設初始的有序序列大小爲1,而後依次擴大這個有序序列。

代碼以下:

/*
    首先有序隊列只包含第一個元素,隨後逐漸擴充其大小,每次將新值與有序隊列末尾的值進行比較,
    找到其位置。
    時間複雜度爲n*n; 
*/
#include <iostream>
using namespace std;
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};
    for(int i=1;i<10;i++){
        if(s[i]>s[i-1]){
            continue;
        }else{
            int j=i-1;
            int val=s[i];
            while(s[j]>val){
                s[j+1]=s[j];
                j--;
            }
            s[j+1]=val;
        }
    }
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

7、冒泡排序

冒泡排序的每次遍歷都會比較臨近元素的大小,不知足要求就交換其順序,每次遍歷都會找到一個當前查找範圍的最大值或最小值。直到查找範圍縮小到1。

代碼以下:

#include <iostream>
using namespace std;
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};//1 1 2 3 4 4 5 6 65 98 
    for(int i=0;i<10;i++){
        for(int j=0;j<9-i;j++){
            if(s[j]>s[j+1]){
                int temp=s[j];
                s[j]=s[j+1];
                s[j+1]=temp;
            }
        }
    }
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

8、簡單選擇排序

簡單選擇排序會在每次遍歷中找到它的最小值或最大值,而後縮小查找的範圍,直到查找範圍縮小到1.

代碼以下:

/*
    每次找出最小值放置,時間複雜度n*n 
*/ 
#include <iostream>
using namespace std;
int main(){
    int s[10]={4,1,5,2,4,3,98,6,65,1};
    for(int i=0;i<10;i++){
        int min=i;
        for(int j=i+1;j<10;j++){
            if(s[j]<s[min]){
                min=j;
            }
        }
        if(min!=i){
            int temp=s[i];
            s[i]=s[min];
            s[min]=temp;
        }
    }
    for(int i=0;i<10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    return 0;
}

總結:

(1)平方階(O(n2))排序
  各種簡單排序:直接插入、直接選擇和冒泡排序;
 (2)線性對數階(O(nlog2n))排序
  快速排序、堆排序和歸併排序;
 (3)O(n1+§))排序,§是介於0和1之間的常數。

       希爾排序
(4)線性階(O(n))排序
  基數排序,此外還有桶、箱排序。

說明:

當原表有序或基本有序時,直接插入排序和冒泡排序將大大減小比較次數和移動記錄的次數,時間複雜度可降至O(n);

而快速排序則相反,當原表基本有序時,將蛻化爲冒泡排序,時間複雜度提升爲O(n2),每次劃分都會將數列劃分爲一個空數組和一個大小減一的數組,故比較次數爲n-1,n-2,n-2,.......1,故爲o(n2)。

原表是否有序,對簡單選擇排序、堆排序、歸併排序和基數排序的時間複雜度影響不大。

 

穩定性:

排序算法的穩定性:若待排序的序列中,存在多個具備相同關鍵字的記錄,通過排序, 這些記錄的相對次序保持不變,則稱該算法是穩定的;若經排序後,記錄的相對 次序發生了改變,則稱該算法是不穩定的。 
     穩定性的好處:排序算法若是是穩定的,那麼從一個鍵上排序,而後再從另外一個鍵上排序,第一個鍵排序的結果能夠爲第二個鍵排序所用。基數排序就是這樣,先按低位排序,逐次按高位排序,低位相同的元素其順序再高位也相同時是不會改變的。另外,若是排序算法穩定,能夠避免多餘的比較;

穩定的排序算法:冒泡排序、插入排序、歸併排序和基數排序

不是穩定的排序算法:選擇排序、快速排序、希爾排序、堆排序

相關文章
相關標籤/搜索