1. 程式人生 > >唔,十種排序

唔,十種排序

 


排序的一些重要效能概念:

 

 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)。