唔,十種排序
排序的一些重要效能概念:
1,排序的穩定性;2,內排序和外排序;
氣泡排序
主要思想:兩兩比較相鄰元素,出現逆序就交換,知道沒有逆序出現為止。從後向前迴圈,模擬冒泡過程。
時間複雜度:
未優化的氣泡排序:未優化的氣泡排序,最壞和平均都是(n^2);
優化後的氣泡排序,最好情況是最好O(n),平均是O(n^2)。
未優化氣泡排序程式碼如下:
void bubbleSort(vector<int> &a){ int n=a.size(); int i=0,j=0; for(i=0;i<n-1;i++) for(j=n-1;j>=i;j--) if(a[j]>a[j+1]) swap(a[j],a[j+1]); }
優化:如果後面已經有序了,就沒必要繼續比較了。如果i==k時 a[k,...,n-1]已經有序,那麼swap動作就沒有發生,說明已經排好序了,可以提前終止外面的for迴圈。
使用標記變數flag指示是否可以提前退出迴圈。
void bubbleSort1(vector<int> &a){ int n=a.size(); int i=0,j=0; bool flag=true;//標記變數 for(i=0;i<n-1&&flag;i++){ flag=false; for(j=n-1;j>=i;j--){ if(a[j]>a[j+1]){ swap(a[j],a[j+1]); flag=true; } } } }
效率分析:
最好的情況是陣列完全正序,只需要比較n-1次,交換0次,O(n);
最壞的情況是陣列完全倒序,比較(n-1)*n/2次,交換(n-1)*n/2次,O(n^2);
選擇排序
主要思想:從前向後迴圈,先找出最小的元素,和第一位元素交換;然後找出次小的,和第二個元素交換,.......
時間複雜度:最好和最壞時間複雜度都是O(n^2);
void selectSort(vector<int> &a){ int n=a.size(); int i=0,j=0; for(i=0;i<n-1;i++){ int min=i; for(j=i+1;j<n;j++){ if(a[min]>a[j]){ min=j; } } if(min!=i) swap(a[i],a[min]); }
效率分析:
最好的情況是陣列完全正序,比較n-1)*n/2次,交換0次,O(n^2);
最壞的情況是陣列完全倒序,比較(n-1)*n/2次,交換(n-1)次,O(n^2);
插入排序
主要思想:類似玩撲克牌的抓牌後手中擺放撲克,令前面元素有序,新來一個元素都在前面有序的部分中找到自己的位置。一邊比較一邊往後移位。
時間複雜度:最好O(n),平均和最壞時間複雜度O(n^2);
虛擬碼:
for j=1 to A.length
key=A[j]
//insert A[j] into the sorted sequence A[0..j-1].
i=j-1
while i>=0 and A[i]>key
A[i+1]=A[i]
i=i-1
A[i+1]=key
C++程式碼如下:
void insertSort(vector<int> &a){
int n=a.size();
for(int j=1;j<n;j++){
int key=a[j];
int i=j-1;
for(;i>=0&&a[i]>key;i--){//往後面移動
a[i+1]=a[i];
}
a[i+1]=key;
}
}
效率分析:
最好的情況是陣列完全正序,只需要比較n-1次,移位0次,O(n);
最壞的情況是陣列完全倒序,比較(n-1)*n/2次,移位(n-1)*(n+2)/2次,O(n^2);
希爾排序(優化的插入排序)
主要思想:根據不同步長對元素進行跳躍式的插入排序,隨著步長逐漸縮小,元素基本有序,插入排序對基本有序的情況效率很高。因而效率較高。
時間複雜度:可以達到O(n^1.5);
void shellSort(vector<int> &a){
int increment=a.size();
int i,j;
do{
increment=increment/3+1;
for(i=increment;i<a.size();i++){
if(a[i]<a[i-increment]){
int key=a[i];
for(j=i-increment;j>=0&&a[j]>key;j-=increment){
a[j+increment]=a[j];
}
a[j+increment]=key;
}
}
}while(increment>1);
}
歸併排序
主要思想:把排好序的子陣列merge起來。
關鍵函式:merge【合併兩個有序陣列,這裡是一個數組的連續的兩部分】
/* 虛擬碼
MERGE(A,l,m,r)
n1=m-l+1
n2=r-m
L[0,...,n1] and R[0,...,n2] are new arrays
for i=0 to n1-1
L[i]=A[l+i]
for j=0 to n2-1
R[j]=A[m+1+j]
L[n1]=INT_MAX
R[n2]=INT_MAX
i=0
j=0
for k=l to r
if L[i]<=R[j]
A[k]=L[i]
i=i+1
else A[k]=R[j]
j=j+1
*/
C++程式碼如下:
void merge(vector<int>& a,int l,int m,int r){
int n1=m-l+1;
int n2=r-m;
int L[n1+1];
int R[n2+1];
int i,j;
for(i=0;i<n1;i++) L[i]=a[l+i];
for(j=0;j<n2;j++) R[j]=a[m+j+1];
L[n1]=INT_MAX;
R[n2]=INT_MAX;
i=0;
j=0;
for(int k=l;k<=r;k++){
if(L[i]<=R[j]){//穩定排序,相等元素前後位置不變
a[k]=L[i++];
}
else a[k]=R[j++];
}
}
遞迴版本的歸併排序:
</pre><pre name="code" class="html">//遞迴版本的歸併
void mergeSortHelper(vector<int>& a,int l,int r){
if(l>=r) return;
int m=(l+r)/2;
mergeSortHelper(a,l,m);
mergeSortHelper(a,m+1,r);
merge(a,l,m,r);
}
void mergeSort(vector<int>& a){
mergeSortHelper(a,0,a.size()-1);
}
非遞迴版本的歸併排序:
//非遞迴版本的歸併
void mergeSortPass(vector<int>& a,int step){
int n=a.size();
int i;
for(i=0;i+2*step-1<n;i+=2*step){
merge(a,i,i+step-1,i+2*step-1);
}
if(i+step<n){//關鍵環節,剩餘部分可以合併
merge(a,i,i+step-1,n-1);//【1】
}
}
void mergeSort1(vector<int>& a){
int n=a.size();
int k=1;
for(;k<n;k*=2){
mergeSortPass(a,k);
}
}
【1】是實現的關鍵一步,防止多餘部分沒有合併的步驟。例如:可以保證9個元素的時候,最後一個元素與前8個合併(step==8)。