因爲博客遷移至www.coderyi.com,文章請看http://www.coderyi.com/archives/412
git
排序分爲內部排序和外部排序,內部排序指待排序的記錄在內存中,外部排序的記錄數量很大,以致於內存放不下而放在外存中,排序過程須要訪問外存。這裏僅介紹內部排序,包括插入排序、交換排序、選擇排序、歸併排序、基數排序。
算法
算法思路:數組{k1,k2,……,kn},排序一開始k1是一個有序序列,讓k2插入獲得一個表長爲2的有序序列,依此類推,最後讓kn插入上述表長爲n-1的有序序列,獲得表長爲n的有序序列。shell
c實現的代碼:編程
// 從小到大排序 int a[]={98,97,34,345,33}; int k=sizeof(a)/sizeof(a[0]); int j; for (int i=1; i<k; i++) { int temp=a[i]; for (j=i-1; j>=0&&a[j]>temp; j--) { a[j+1]=a[j]; } a[j+1]=temp; }
算法思路:當直接插入進行到某一趟時,對於r[i]來說,前面i-1個記錄已經按關鍵字有序。此時不用直接插入排序的方法,而改成折半查找,找出r[i]應插入的位置。windows
c實現的代碼:數組
// 從小到大排序 void binasort(int r[100],int n){ for (int i=1; i<n; i++) { int temp =r[i]; int low=0; int high=i-1; while (low<=high) { int middle=(low+high)/2; if (temp<r[middle]) { high=middle-1; }else{ low=middle+1; } } for (int j=i-1; j>=low; j--) { r[j+1]=r[j]; } r[low]=temp; } }
算法思路:「縮小增量」的排序方法,初期選用增量較大間隔比較,而後增量縮小,最後爲1,希爾排序對增量序列沒有嚴格規定。設有組關鍵字{99,22,33,333,2,3,23,44},由小到大排序,這裏n=8,先第一個個增量取d1=4,那麼記錄分爲4組,第一組r[0],r[4],第二組r[1],r[5],……在各組內部使用插入排序,使得每組內是有序的,接着取d2=2,分爲兩組,d3=1,最後就編程有序序列。函數
c語言實現的代碼:ui
// 從小到大排序 void shellsort(int r[100],int n){ int k=n/2; while (k>0) { for (int i=k; i<n; i++) { int temp=r[i]; int j=i-k; while ((r[j]>temp)&&(j>=0)) { r[j+k]=r[j]; j=j-k; } r[j+k]=temp; } k/=2; } }
算法思路:在排序過程,關鍵字較小的記錄通過與其餘記錄的對比交換,好像水中的氣泡同樣,移到數據序列的最前面。spa
c語言實現的代碼:.net
// 從小到大排序 void bubblesort(int r[100],int n){ for (int i=0; i<n-1; i++) { for (int j=0; j<n-1-i; j++) { if (r[j]>r[j+1]) { int temp=r[j]; r[j]=r[j+1]; r[j+1]=temp; } } } }
算法思路:經過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的全部數據比另一部分的全部數據都要小,而後再按此方法對這兩部分數據分別進行快速排序。整個排序過程能夠遞歸實現,也能夠非遞歸實現。
c語言實現遞歸的快速排序的代碼:
// 從小到大排序 void quickSort(int a[],int numsize){ int i=0,j=numsize-1; int val=a[0];//指定參考值val大小 if (numsize>1) { //確保數組長度至少爲2,不然無需排序 while (i<j) {//循環結束條件 // 從後向前搜索比val小的元素,找到後填到a[i]中並跳出循環 for (; j>i; j--) { if (a[j]<val) { a[i]=a[j]; break; } } // 從前向後搜索比val大的元素,找到後填到a[j]中並跳出循環 for (; i<j; i++) { if (a[i]>val) { a[j]=a[i]; break; } } } a[i]=val;//將保存再val中的數放到a[i]中 quickSort(a, i);//遞歸,對前i個數排序 quickSort(a+i+1, numsize-1-i);//對i+1到numsize-1-i個數排序 } }
算法思路:對於一組關鍵字{k1,k2,……kn},將其從小到大排序,首先從k1,k2,……k3中選擇最小值Kz,在將Kz與k1對換;而後從k2……Kn中選最小值Kz,與k2交換。如此選擇和調換n-2趟,第n-1趟只要調換Kn-1 和Kn比較就行了。
c語言實現的代碼:
//從小到大排序 void sisort(int r[100],int n){ for (int i=0; i<n-1; i++) { int z=i; for (int j=i+1; j<n; j++) { if (r[z]>r[j]) { z=j; } } if (z!=i) { int temp=r[i]; r[i]=r[z]; r[z]=temp; } } }
算法思路:堆有兩個性質,一是堆中某個節點的值老是不大於或不小於其父節點的值,二是堆是一棵徹底樹。以從大到小排序爲例,首先要把獲得的數組構建爲一個最小堆,這樣父節點均是小於或者等於子結點,根節點就是最小值,而後讓根節點與尾節點交換,這樣一次以後,再把前n-1個元素構建出最小根堆,讓根結點與第n-2個元素交換,依此類推,獲得降序序列。
c語言實現代碼:
//從大到小排序 //以i節點爲根,調整爲堆的算法,m是節點總數,i節點的子結點爲i*2+1,i*2+2 void heapMin(int r[100],int i,int m){ // temp保存根節點,j爲左孩子編號 int j,temp; temp=r[i]; j=2*i+1; while (j<m) { if (j+1<m && r[j+1]<r[j]) {//在左右孩子中找最小的 j++; } if (r[j]>=temp) { break; } r[i]=r[j]; i=j; j=2*i+1; } r[i]=temp; } void heapSort(int r[100],int n){ // n/2-1最後一個非葉子節點 // 下面這個操做是創建最小堆 for (int i=n/2-1; i>=0; i--) { heapMin(r, i, n); } // 一下for語句爲輸出堆頂元素,調整堆操做 for (int j=n-1; j>=1; j--) { // 堆頂與堆尾交換 int temp=r[0]; r[0]=r[j]; r[j]=temp; heapMin(r, 0, j); } //獲得的就是降序序列 for (int i=0; i<n; i++) { printf(" %d",r[i]); } }
時間複雜度:O(n log2n)
參考網址:http://blog.csdn.net/morewindows/article/details/6709644
算法思路:它指的是將兩個順序序列合併成一個順序序列的方法。若有數列{6,202,100,301,38,8,1},第一次歸併後變成了{6,202},{100,301},{8,38},{1};第二次歸併後,{6,100,202,301},{1,8,38};第三次歸併後{1,6,8,38,100,202,301}。
代碼實現分三步,經過自底向上實現歸併子算法,一趟歸併掃描子算法,二路歸併排序算法
#include <stdlib.h> #include <stdio.h> //歸併子算法 //將有序的X[s..u]和X[u+1..v]歸併爲有序的Z[s..v] void merge(int X[], int Z[], int s, int u, int v) { int i, j, q; i = s; j = u + 1; q = s; while( i <= u && j<= v ) { if( X[i] <= X[j] ) Z[q++] = X[i++]; else Z[q++] = X[j++]; } while( i <= u ) //將X中剩餘元素X[i..u]複製到Z Z[q++] = X[i++]; while( j <= v ) //將X中剩餘元素X[j..v]複製到Z Z[q++] = X[j++]; } /* 一趟歸併掃描子算法 將參加排序的序列分紅若干個長度爲 t 的,且各自按值有序的子序列,而後屢次調用歸併子算法merge將全部兩兩相鄰成對的子序列合併成若干個長度爲2t 的,且各自按值有序的子序列。 若某一趟歸併掃描到最後,剩下的元素個數不足兩個子序列的長度時: 若剩下的元素個數大於一個子序列的長度 t 時,則再調用一次歸併子算法 merge 將剩下的兩個不等長的子序列合併成一個有序子序列; 若剩下的元素個數小於或者等於一個子序列的長度 t 時,只須將剩下的元素依次複製到前一個子序列後面。 */ /* X[0..n-1]表示參加排序的初始序列 * t爲某一趟歸併時子序列的長度 * 整型變量i指出當前歸併的兩個子序列中第1個子序列的第1個元素的位置 * Y[0..n-1]表示這一趟歸併後的結果 */ void mergePass(int X[], int Y[], int n, int t) { int i = 0, j; while( n - i >= 2 * t ) //將相鄰的兩個長度爲t的各自有序的子序列合併成一個長度爲2t的子序列 { merge(X, Y, i, i + t - 1, i + 2 * t - 1); i = i + 2 * t; } if( n - i > t ) //若最後剩下的元素個數大於一個子序列的長度t時 merge(X, Y, i, i + t - 1, n - 1); else //n-i <= t時,至關於只是把X[i..n-1]序列中的數據賦值給Y[i..n-1] for( j = i ; j < n ; ++j ) Y[j] = X[j]; } //二路歸併排序算法 void mergeSort(int X[], int n) { int t = 1; int *Y = (int *)malloc(sizeof(int) * n); while( t < n ) { mergePass(X, Y, n, t); t *= 2; mergePass(Y, X, n, t); t *= 2; } free(Y); } void print_array(int array[], int n) { int i; for( i = 0 ; i < n ; ++i ) printf("%d ", array[i]); printf("\n"); } int main() { int array[] = {65, 2, 6, 1, 90, 78, 105, 67, 35, 23, 3, 88, -22}; int size = sizeof(array) / sizeof(int); mergeSort(array, size); print_array(array, size); return 0; }
時空複雜度:二路歸併排序算法:將參加排序的初始序列分紅長度爲1的子序列使用mergePass函數進行第一趟排序,獲得 n / 2 個長度爲 2 的各自有序的子序列(若n爲奇數,還會存在一個最後元素的子序列),再一次調用mergePass函數進行第二趟排序,獲得 n / 4 個長度爲 4 的各自有序的子序列, 第 i 趟排序就是兩兩歸併長度爲 2^(i-1) 的子序列獲得 n / (2^i) 長度爲 2^i 的子序列,直到最後只剩一個長度爲n的子序列。由此看出,一共須要 log2n 趟排序,每一趟排序的時間複雜度是 O(n), 由此可知
該算法的總的時間複雜度是是 O(n log2n),可是該算法須要 O(n) 的輔助空間,空間複雜度很大,是 O(n).
算法思路:
基數排序能夠採用LSD(Least significant digital)或者MSD(Most significant digital),LSD的排序由鍵值的最右邊開始,MSD從最左邊開始。
以LSD爲例,假設原來有一串數值以下所示:
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
第一步
首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中:
0
1 81
2 22
3 73 93 43
4 14
5 55 65
6
7
8 28
9 39
第二步
接下來將這些桶子中的數值從新串接起來,成爲如下的數列:
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
接着再進行一次分配,此次是根據十位數來分配:
0
1 14
2 22 28
3 39
4 43
5 55
6 65
7 73
8 81
9 93
第三步
接下來將這些桶子中的數值從新串接起來,成爲如下的數列:
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
這時候整個數列已經排序完畢;若是排序的對象有三位數以上,則持續進行以上的動做直至最高位數爲止。
LSD的基數排序適用於位數小的數列,若是位數多的話,使用MSD的效率會比較好。