八種常見的演算法排序
一、排序演算法的分類(列舉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個節點。
堆是具有以下性質的完全二叉樹:
- 每個結點的值都大於或等於其左右孩子結點的值,稱為大根堆;
- 或者每個結點的值都小於或等於其左右孩子結點的值,稱為小根堆。