本人介紹的排序算法主要有:插入排序,選擇排序,冒泡排序,快速排序,堆排序,歸並排序,希爾排序,二叉樹排序,桶排序,基數排序(后兩者為非比較排序,前面的為比較排序)。
排序的穩定性和復雜度:
不穩定:
選擇排序(selection sort)— O(n2)
快速排序(quicksort)— O(nlogn) 平均時間, O(n2) 最壞情況; 對於大的、亂序串列一般認為是最快的已知排序
堆排序 (heapsort)— O(nlogn)
希爾排序 (shell sort)— O(nlogn)
基數排序(radix sort)— O(n·k); 需要 O(n) 額外存儲空間 (K為特征個數)
穩定:
插入排序(insertion sort)— O(n2)
冒泡排序(bubble sort) — O(n2)
歸並排序 (merge sort)— O(nlogn); 需要 O(n) 額外存儲空間
二叉樹排序(Binary tree sort) — O(nlogn); 需要 O(n) 額外存儲空間
桶排序 (bucket sort)— O(n); 需要 O(k) 額外存儲空間
1、插入排序
對於一個序列{a[0]……a[n]},當記錄值是第i個元素時,前面i-1個元素已經排好序了,那么這個記錄值從第i-1個元素一直往前比較,找到屬於它的位置后插進去。

1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int a[]={1,99,2,88,3,77,4,66}; 7 int n=sizeof(a)/4; 8 for(int i=0; i<n; i++) 9 { 10 int tp=a[i], j; 11 for(j=i-1; j>=0&&a[j]>tp; j--) a[j+1]=a[j]; 12 a[j+1]=tp; 13 } 14 cout << a[0] ; 15 for(int i=1; i<n; i++) cout << " " << a[i]; 16 cout <<endl; 17 return 0; 18 }
2、選擇排序
對於一個序列{a[0]……a[n]},前面i-1個元素都是已經排好序的,那么從第i到第n個元素,找到最小值的那個元素,如果下標不是i,則讓第i個元素和那個最小的元素位置互換。

1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int a[]={1,99,2,88,3,77,4,66}; 7 int n=sizeof(a)/4; 8 for(int i=0; i<n; i++) 9 { 10 int pos=-1, minn=a[i]; 11 for(int j=i+1; j<n; j++) 12 { 13 if(a[j]<minn) minn=a[j], pos=j; 14 } 15 if(pos!=-1) swap(a[i],a[pos]); 16 } 17 cout << a[0] ; 18 for(int i=1; i<n; i++) cout << " " << a[i]; 19 cout <<endl; 20 return 0; 21 }
3、冒泡排序
冒泡排序顧名思義就是從最后往前兩個元素開始進行兩兩比較,如果a[i]小於a[i-1],那么讓他們互換位置,每比較一輪必有一個最小的元素冒泡到這些所比較元素的前面。

1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 int a[]={1,99,2,88,3,77,4,66}; 7 int n=sizeof(a)/4; 8 for(int i=0; i<n; i++) 9 { 10 for(int j=n-1; j>i; j--) 11 if(a[j]<a[j-1]) swap(a[j],a[j-1]); 12 } 13 cout << a[0] ; 14 for(int i=1; i<n; i++) cout << " " << a[i]; 15 cout <<endl; 16 return 0; 17 }
4、快速排序
基本思想就是取一個數作為中間數(一般是取第一個數作為中間數),小於它的都放到左邊,大於它的都放到右邊,再對每一邊利用同樣的思想進行處理。

1 #include <iostream> 2 using namespace std; 3 4 void QuickSort(int *a, int l, int r) 5 { 6 if(a==NULL||l>=r) return ; 7 8 int i=l, j=r, tmp=a[l]; 9 while(i<j) 10 { 11 while(j>i&&a[j]>=tmp) j--; 12 a[i]=a[j]; 13 while(i<j&&a[i]<=tmp) i++; 14 a[j]=a[i]; 15 } 16 a[i]=tmp; 17 QuickSort(a,l,i-1); 18 QuickSort(a,i+1,r); 19 } 20 21 int main() 22 { 23 int a[]= {1,99,2,88,3,77,4,66}; 24 int n=sizeof(a)/4; 25 QuickSort(a,0,n-1); 26 cout << a[0] ; 27 for(int i=1; i<n; i++) cout << " " << a[i]; 28 cout <<endl; 29 return 0; 30 }
5、堆排序
堆排序其實要利用到二叉堆,二叉堆其實完全可以理解為一顆有限制的完全二叉樹。
二叉堆的定義:二叉堆可以分為最大堆和最小堆。最大堆為對於所有節點它的左右節點權值一定比它小,最小堆為對於所有節點它的左右節點權值一定比它大。
二叉堆的插入:將一個序列下表從0開始一個一個往堆里插入,因為滿足完全二叉樹性質,所以這么做是可行的。對於插入的第i個數,那么從下往上,它的父親節點為(i-1)/2個數,再根據二叉堆的性質進行調整。
二叉堆的刪除:每次進行一次堆調整之后,根節點必是最大的(最大堆),每次把根節點a[0]取出和數組第n個數互換,然后再用數組第1個到第n-1個數再次建堆,如此反復取出再建堆,那么得到的新序列必是一個有序序列。

1 #include <iostream> 2 using namespace std; 3 4 void BuildHeap(int *a, int i, int n) //建二叉堆 5 { 6 int j=(i-1)/2; //j為i節點的父親節點 7 while(i>0) 8 { 9 if(a[j]>=a[i]) break; 10 swap(a[i],a[j]); 11 i=j; 12 j=(i-1)/2; 13 } 14 } 15 16 void MaxHeapSort(int *a, int i, int n) //二叉堆排序 17 { 18 int j=2*i+1, tmp=a[i]; 19 while(j<n) 20 { 21 if(a[j+1]>a[j]&&j+1<n) j++; //選出i節點左右孩子節點的最大值 22 if(tmp>=a[j]) break; 23 a[i]=a[j]; 24 i=j; 25 j=2*i+1; 26 } 27 a[i]=tmp; 28 } 29 30 31 int main() 32 { 33 int a[]= {1,99,2,88,3,77,4,66}; 34 int n=sizeof(a)/4; 35 for(int i=0; i<=n-1; i++) 36 BuildHeap(a,i,n); 37 for(int i=n-1; i>=1; i--) 38 { 39 swap(a[i],a[0]); 40 MaxHeapSort(a,0,i); 41 } 42 cout << a[0] ; 43 for(int i=1; i<n; i++) cout << " " << a[i]; 44 cout <<endl; 45 return 0; 46 }
6、歸並排序
歸並的意思就是兩個或兩個以上的有序表組合成一個新的有序表。整個歸並排序需要進行【lgn取上限】次,總的時間復雜度為O(nlgn)。與快速排序相比,歸並排序的最大特點是:它是一種穩定的排序方法。

1 #include <iostream> 2 using namespace std; 3 4 void Merge(int *a, int s, int m, int t) 5 { 6 int *b=new int[10]; 7 int i=s, j=m+1, num=0; 8 while(i<=m&&j<=t) 9 { 10 if(a[i]<=a[j]) b[num++]=a[i], i++; 11 else b[num++]=a[j],j++; 12 } 13 while(i<=m) b[num++]=a[i], i++; 14 while(j<=t) b[num++]=a[j], j++; 15 for(int i=s; i<=t; i++) a[i]=b[i-s]; 16 delete[] b; 17 } 18 19 void MergeSort(int *a, int s, int t) 20 { 21 if(a==NULL) return ; 22 if(s<t) 23 { 24 int mid=(s+t)/2; 25 MergeSort(a,s,mid); 26 MergeSort(a,mid+1,t); 27 Merge(a,s,mid,t); 28 } 29 } 30 31 32 int main() 33 { 34 int a[]= {1,99,2,88,3,77,4,66}; 35 int n=sizeof(a)/4; 36 MergeSort(a,0,n-1); 37 cout << a[0] ; 38 for(int i=1; i<n; i++) cout << " " << a[i]; 39 cout <<endl; 40 return 0; 41 }
7、希爾排序
很多人都說希爾排序是插入排序的一種改進,我看了半天也沒看明白這句話。
希爾排序就是利用無空位的跳躍值gap進行跳躍排序,如果n為8,為gap的取值則為8 4 2 1, gap=gap/2,gap初始值為n/2。
對於每個gap值,都要從后往前掃一遍(以gap值大小跳躍比較),即i,i-gap,i-2*gap……。注意:只限在相鄰兩個掃到的數比較。
時間復雜度:O(nlog(n*n))。

1 #include <iostream> 2 using namespace std; 3 4 void ShellSort(int *a, int n) 5 { 6 for(int gap=n/2; gap>0; gap/=2) 7 for(int i=gap; i<n; i++) 8 for(int j=i-gap; j>=0; j-=gap) 9 if(a[j]>a[j+gap]) swap(a[j],a[j+gap]); 10 } 11 12 int main() 13 { 14 int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428}; 15 int n=sizeof(a)/4; 16 ShellSort(a,n); 17 cout << a[0]; 18 for(int i=1; i<n; i++) cout << " " << a[i]; 19 cout << endl; 20 return 0; 21 }
8、二叉樹排序
二叉樹的性質:對於每個節點,它的左孩子的鍵值一定比它小,右孩子的鍵值一定比它大。
二叉樹排序簡單點說就是先隨便設置一個根節點,然后將其他數一個一個插入到樹中,權值小於此節點則往左走,大於往右走,一直找到合適的位置建立自己新的節點位置。

1 #include <iostream> 2 #include <cstring> 3 #include <sstream> 4 #include <algorithm> 5 using namespace std; 6 7 struct Node 8 { 9 int key; 10 Node *l, *r; 11 Node(){ l=NULL; r=NULL;} 12 }; 13 14 Node* Insert(Node *rt, int key) 15 { 16 if(rt==NULL) 17 { 18 Node *rt=new Node(); 19 rt->key=key; 20 return rt; 21 } 22 if(key<rt->key) rt->l=Insert(rt->l,key); 23 else rt->r=Insert(rt->r,key); 24 return rt; 25 } 26 27 void Printf(Node *rt) 28 { 29 if(rt->l!=NULL) Printf(rt->l); 30 cout << rt->key << " "; 31 if(rt->r!=NULL) Printf(rt->r); 32 } 33 34 int main() 35 { 36 int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428}; 37 int n=sizeof(a)/4; 38 Node *root=new Node(); 39 root->key=a[0]; 40 for(int i=1; i<n; i++) 41 { 42 Insert(root,a[i]); 43 } 44 Printf(root); 45 cout << endl; 46 return 0; 47 }
9、基數排序
基數是一種不穩定的排序,它的時間復雜度為O(k*n),k表示最大數的位數,所以當一個序列中有一個很大很大的數時,它排序所花費的時間是非常高昂的。
基數排序的原理是一位一位來排序:先按個位大小排序,再按十位大小排序,接着百位……,一直排到最大位數停止。
比如這樣一個數列排序: 342 ,58, 576, 356
第一次排序(個位):
3 4 2
5 7 6
3 5 6
0 5 8
第二次排序(十位):
3 4 2
3 5 6
0 5 8
5 7 6
第三次排序(百位):
0 5 8
3 4 2
3 5 6
5 7 6
結果: 58 342 356 576。

1 #include <iostream> 2 #include <cstring> 3 #include <sstream> 4 #include <algorithm> 5 using namespace std; 6 7 int maxdigit(int *a, int n) //返回數組中最大數的位數 8 { 9 int maxx=0; 10 for(int i=0; i<n; i++) 11 { 12 stringstream sa; 13 sa<<a[i]; 14 string s=sa.str(); 15 maxx=max(maxx,int(s.size())); 16 } 17 return maxx; 18 } 19 20 void BaseSort(int *a, int n) 21 { 22 int *count=new int[10]; 23 int *tmp=new int[n]; 24 int m=maxdigit(a,n); 25 int base=1; 26 for(int i=1; i<=m; i++) 27 { 28 for(int j=0; j<10; j++) count[j]=0; 29 for(int j=0; j<n; j++) 30 { 31 int k=a[j]/base%10; 32 count[k]++; 33 } 34 for(int j=1; j<10; j++) 35 count[j]+=count[j-1]; 36 for(int j=n-1; j>=0; j--) 37 { 38 int k=a[j]/base%10; 39 count[k]--; 40 tmp[ count[k] ]=a[j]; 41 } 42 for(int j=0; j<n; j++) a[j]=tmp[j]; 43 base*=10; 44 } 45 delete[] count; 46 delete[] tmp; 47 } 48 49 int main() 50 { 51 int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428}; 52 int n=sizeof(a)/4; 53 BaseSort(a,n); 54 cout << a[0] ; 55 for(int i=1; i<n; i++) cout << " " << a[i]; 56 cout <<endl; 57 return 0; 58 }