1. 程式人生 > >八種常見的演算法排序

八種常見的演算法排序

一、排序演算法的分類(列舉7種): 

1.氣泡排序 
2.選擇排序 
3.插入排序 
4.快速排序 
5.歸併排序  (歸併排序需要額外的記憶體空間來儲存資料,其他的方式都是在原來資料上做交換) 
6.希爾排序 
7.堆排序

1、最基礎的排序——氣泡排序 (時間複雜度是 O(N^2)

設無序陣列a[]長度為N,以由小到大排序為例。冒泡的原理: 
1.比較相鄰的前兩個資料,如果前面的資料a[0]大於後面的資料a[1] (為了穩定性,等於不交換),就將前面兩個資料進行交換。在將計數器 i ++; 
2.當遍歷完N個數據一遍後,最大的資料就會沉底在陣列最後a[N-1]。 
3.然後N=N-1;再次進行遍歷排序將第二大的資料沉到倒數第二位置上a[N-2]。再次重複,直到N=0;將所有資料排列完畢。

無序陣列: 2 5 4 7 1 6 8 3

遍歷1次後: 2 4 5 1 6 7 3 8

遍歷2次後: 2 4 1 5 6 3 7 8

遍歷3次後: 2 4 1 5 3 6 7 8

...

遍歷7次後: 1 2 3 4 5 6 7 8

void BubbleSore(int *array, int n) //優化
{   
    int i = n;
    int j = 0;
    int temp = 0;
    Boolean flag = TRUE; 

    while(flag){
    flag = FALSE;  
        for(j = 1; j < i; ++j){
            if(array[j - 1] > array[j]){
            temp = array[j-1];
            array[j - 1] = array[j];
            array[j] = temp;
            flag = TRUE;
            }           
         }
         i--;
    }   
}

 2、最易理解的排序——選擇排序 (時間複雜度O(N^2))

 原理:

遍歷一遍找到最小的,與第一個位置的數進行交換。再遍歷一遍找到第二小的,與第二個位置的數進行交換。

無序陣列: 2 5 4 7 1 6 8 3

遍歷1次後: 1 5 4 7 2 6 8 3

遍歷2次後: 1 2 4 7 5 6 8 3

...

遍歷7次後: 1 2 3 4 5 6 7 8

void Selectsort(int *array, int n)
{
    int i = 0;
    int j = 0;
    int min = 0;
    int temp = 0;    

    for(i; i < n; i++){
        min = i;
        for(j = i + 1; j < n; j++){
        if(array[min] > array[j])
            min = j;
        }
        temp = array[min];
        array[min] = array[i];
        array[i] = temp; 
    }
}

3、撲克牌法排序——插入排序 (時間複雜度是O(N^2)

插入的原理: 
1.初始時,第一個資料a[0]自成有序陣列,後面的a[1]~a[N-1]為無序陣列。令 i = 1; 
2.將第二個資料a[1]加入有序序列a[0]中,使a[0]~a[1]變為有序序列。i++; 
3.重複迴圈第二步,直到將後面的所有無序數插入到前面的有序數列內,排序完成。

無序陣列: 2 | 5 4 7 1 6 8 3

遍歷1次後: 2 5 | 4 7 1 6 8 3

遍歷2次後: 2 4 5 | 7 1 6 8 3

遍歷3次後: 2 4 5 7 | 1 6 8 3

...

插入排序是一種比較快的排序,因為它每次都是和有序的數列進行比較插入,所以每次的比較很有”意義”,導致交換次數較少,所以插入排序在O(N^2)級別的排序中是比較快的排序演算法。 

{
    int i = 0;
    int j = 0;
    int temp = 0;   

    for(i = 1; i < n; i++){
        if(array[i] < array[i-1]){
            temp = array[i]; 
        for(j = i - 1; j >= 0 && array[j] > temp; j--){   
            array[j+1] = array[j];
            }
            array[j+1] = temp;
        }
    } 
}

4、最快的排序——快速排序(時間複雜度O(N * log N))

原理:

1、先從數列中取出一個數作為基準數

2、分割槽過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊

3、再對左右區間重複第二步,直到各區間只有一個數

5、分而治之——歸併排序 (時間複雜度O(N * log N))

原理:

歸併就是採用這種操作,首先將有序數列一分二,二分四……直到每個區都只有一個數據,可以看做有序序列。然後進行合併,每次合併都是有序序列在合併,所以效率比較高。

無序陣列: 2 5 4 7 1 6 8 3

第一步拆分:2 5 4 7 | 1 6 8 3

第二步拆分:2 5 | 4 7 | 1 6 | 8 3

第三步拆分:2 | 5 | 4 | 7 | 1 | 6 | 8 | 3

第一步合併:2 5 | 4 7 | 1 6 | 3 8

第二步合併:2 4 5 7 | 1 3 6 8

第三步合併:1 2 3 4 5 6 7 8

 6、縮小增量——希爾排序 (時間複雜度O(N * log N))
希爾排序的實質就是分組插入排序,該方法又稱為縮小增量排序,原理是:

希爾排序:將無序陣列分割為若干個子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,對各個子序列進行插入排序;然後再選擇一個更小的增量,再將陣列分割為多個子序列進行排序......最後選擇增量為1,即使用直接插入排序,使最終陣列成為有序。

增量的選擇:在每趟的排序過程都有一個增量,至少滿足一個規則 增量關係 d[1] > d[2] > d[3] >..> d[t] = 1 (t趟排序);根據增量序列的選取其時間複雜度也會有變化,這個不少論文進行了研究,在此處就不再深究;本文采用首選增量為n/2,以此遞推,每次增量為原先的1/2,直到增量為1;

7、堆排序(時間複雜度為 O(N*logN))

完全二叉樹的特點是:

1)只允許最後一層有空缺結點且空缺在右邊,即葉子結點只能在層次最大的兩層上出現;

2)對任一結點,如果其右子樹的深度為j,則其左子樹的深度必為j或j+1。 即度為1的點只有1個或0個

完全二叉樹第i層至多有2^(i-1)個節點,共i層的完全二叉樹最多有2^i-1個節點。

堆是具有以下性質的完全二叉樹:

  • 每個結點的值都大於或等於其左右孩子結點的值,稱為大根堆;
  • 或者每個結點的值都小於或等於其左右孩子結點的值,稱為小根堆。