1. 程式人生 > >十大排序演算法總結

十大排序演算法總結

排序演算法有很多,所以在特定情景中使用哪一種演算法很重要。為了選擇合適的演算法,可以按照建議的順序考慮以下標準: 
(1)執行時間 
(2)儲存空間 
(3)程式設計工作 
   對於資料量較小的情形,(1)(2)差別不大,主要考慮(3);而對於資料量大的,(1)為首要。 

主要排序法有: 
一、冒泡(Bubble)排序——相鄰交換 
二、選擇排序——每次最小/大排在相應的位置 
三、插入排序——將下一個插入已排好的序列中 
四、殼(Shell)排序——縮小增量 
五、歸併排序 
六、快速排序 
七、堆排序 
八、拓撲排序 
九、錦標賽排序 
十、基數排序 



一、冒泡(Bubble)排序 

----------------------------------Code 從小到大排序n個數------------------------------------ 

void BubbleSortArray() 

      for(int i=1;i<n;i++) 
      { 
        for(int j=0;i<n-i;j++) 
         { 
              if(a[j]>a[j+1])//比較交換相鄰元素 
               { 
                   int temp; 
                   temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; 
               } 
         } 
      } 

-------------------------------------------------Code------------------------------------------------ 

效率 O(n²),適用於排序小列表。 


二、選擇排序 
----------------------------------Code 從小到大排序n個數-------------------------------- 
void SelectSortArray() 

    int min_index; 
    for(int i=0;i<n-1;i++) 
    { 
         min_index=i; 
         for(int j=i+1;j<n;j++)//每次掃描選擇最小項 
            if(arr[j]<arr[min_index])  min_index=j; 

         if(min_index!=i)//找到最小項交換,即將這一項移到列表中的正確位置 
         { 
             int temp; 
             temp=arr[i]; arr[i]=arr[min_index]; arr[min_index]=temp; 



-------------------------------------------------Code----------------------------------------- 
效率O(n²),適用於排序小的列表。 


三、插入排序 
--------------------------------------------Code 從小到大排序n個數------------------------------------- 
void InsertSortArray() 

for(int i=1;i<n;i++)//迴圈從第二個陣列元素開始,因為arr[0]作為最初已排序部分 

    int temp=arr[i];//temp標記為未排序第一個元素 
    int j=i-1; 
while (j>=0 && arr[j]>temp)/*將temp與已排序元素從小到大比較,尋找temp應插入的位置*/ 

    arr[j+1]=arr[j]; 
    j--; 

arr[j+1]=temp; 


------------------------------Code-------------------------------------------------------------- 
最佳效率O(n);最糟效率O(n²)與冒泡、選擇相同,適用於排序小列表 
若列表基本有序,則插入排序比冒泡、選擇更有效率。 


四、殼(Shell)排序——縮小增量排序 
-------------------------------------Code 從小到大排序n個數------------------------------------- 
void ShellSortArray() 

  for(int incr=3;incr<0;incr--)//增量遞減,以增量3,2,1為例 

       for(int L=0;L<(n-1)/incr;L++)//重複分成的每個子列表 

   for(int i=L+incr;i<n;i+=incr)//對每個子列表應用插入排序 
   { 
      int temp=arr[i]; 
      int j=i-incr; 
      while(j>=0&&arr[j]>temp) 
      { 
          arr[j+incr]=arr[j]; 
          j-=incr; 

arr[j+incr]=temp; 




--------------------------------------Code------------------------------------------- 
適用於排序小列表。 
效率估計O(nlog2^n)~O(n^1.5),取決於增量值的最初大小。建議使用質數作為增量值,因為如果增量值是2的冪,則在下一個通道中會再次比較相同的元素。 
殼(Shell)排序改進了插入排序,減少了比較的次數。是不穩定的排序,因為排序過程中元素可能會前後跳躍。 


五、歸併排序 
----------------------------------------------Code 從小到大排序--------------------------------------- 
void MergeSort(int low,int high) 

   if(low>=high)   return;//每個子列表中剩下一個元素時停止 
   else int mid=(low+high)/2;/*將列表劃分成相等的兩個子列表,若有奇數個元素,則在左邊子列表大於右側子列表*/ 
   MergeSort(low,mid);//子列表進一步劃分 
   MergeSort(mid+1,high); 
   int [] B=new int [high-low+1];//新建一個數組,用於存放歸併的元素 
   for(int i=low,j=mid+1,k=low;i<=mid && j<=high;k++)/*兩個子列表進行排序歸併,直到兩個子列表中的一個結束*/ 
   { 
       if (arr[i]<=arr[j];) 

    B[k]=arr[i]; 
    I++; 

else
    { B[k]=arr[j]; j++; } 

for(   ;j<=high;j++,k++)//如果第二個子列表中仍然有元素,則追加到新列表 
      B[k]=arr[j]; 
   for(   ;i<=mid;i++,k++)//如果在第一個子列表中仍然有元素,則追加到新列表中 
      B[k]=arr[i]; 
   for(int z=0;z<high-low+1;z++)//將排序的陣列B的 所有元素複製到原始陣列arr中 
      arr[z]=B[z]; 

-----------------------------------------------------Code--------------------------------------------------- 
效率O(nlogn),歸併的最佳、平均和最糟用例效率之間沒有差異。 
適用於排序大列表,基於分治法。 

六、快速排序 
------------------------------------Code-------------------------------------------- 
/*快速排序的演算法思想:選定一個樞紐元素,對待排序序列進行分割,分割之後的序列一個部分小於樞紐元素,一個部分大於樞紐元素,再對這兩個分割好的子序列進行上述的過程。*/                  void swap(int a,int b){int t;t =a ;a =b ;b =t ;} 
        int Partition(int [] arr,int low,int high) 
        { 
            int pivot=arr[low];//採用子序列的第一個元素作為樞紐元素 
            while (low < high) 
            { 
                //從後往前栽後半部分中尋找第一個小於樞紐元素的元素 
                while (low < high && arr[high] >= pivot) 
                { 
                    --high; 
                } 
                //將這個比樞紐元素小的元素交換到前半部分 
                swap(arr[low], arr[high]); 
                //從前往後在前半部分中尋找第一個大於樞紐元素的元素 
                while (low <high &&arr [low ]<=pivot ) 
                { 
                    ++low ; 
                } 
                swap (arr [low ],arr [high ]);//將這個樞紐元素大的元素交換到後半部分 
            } 
            return low ;//返回樞紐元素所在的位置 
        } 
        void QuickSort(int [] a,int low,int high) 
        { 
            if (low <high ) 
            { 
                int n=Partition (a ,low ,high ); 
                QuickSort (a ,low ,n ); 
                QuickSort (a ,n +1,high ); 
            } 
        } 
----------------------------------------Code------------------------------------- 
平均效率O(nlogn),適用於排序大列表。 
此演算法的總時間取決於樞紐值的位置;選擇第一個元素作為樞紐,可能導致O(n²)的最糟用例效率。若數基本有序,效率反而最差。選項中間值作為樞紐,效率是O(nlogn)。 
基於分治法。 



七、堆排序 
最大堆:後者任一非終端節點的關鍵字均大於或等於它的左、右孩子的關鍵字,此時位於堆頂的節點的關鍵字是整個序列中最大的。 
思想: 
(1)令i=l,並令temp= kl ; 
(2)計算i的左孩子j=2i+1; 
(3)若j<=n-1,則轉(4),否則轉(6); 
(4)比較kj和kj+1,若kj+1>kj,則令j=j+1,否則j不變; 
(5)比較temp和kj,若kj>temp,則令ki等於kj,並令i=j,j=2i+1,並轉(3),否則轉(6) 
(6)令ki等於temp,結束。 
-----------------------------------------Code--------------------------- 
void HeapSort(SeqIAst R) 

    { //對R[1..n]進行堆排序,不妨用R[0]做暫存單元    int I;    BuildHeap(R); //將R[1-n]建成初始堆for(i=n;i>1;i--) //對當前無序區R[1..i]進行堆排序,共做n-1趟。{      R[0]=R[1]; R[1]=R[i]; R[i]=R[0]; //將堆頂和堆中最後一個記錄交換      Heapify(R,1,i-1);  //將R[1..i-1]重新調整為堆,僅有R[1]可能違反堆性質     }    } ---------------------------------------Code-------------------------------------- 


堆排序的時間,主要由建立初始堆和反覆重建堆這兩部分的時間開銷構成,它們均是通過呼叫Heapify實現的。 

      堆排序的最壞時間複雜度為O(nlgn)。堆排序的平均效能較接近於最壞效能。     由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的檔案。     堆排序是就地排序,輔助空間為O(1),     它是不穩定的排序方法。 


堆排序與直接插入排序的區別: 
     直接選擇排序中,為了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,然後在R[2..n]中選出關鍵字最小的記錄,又需要做n-2次比較。事實上,後面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,但由於前一趟排序時未保留這些比較結果,所以後一趟排序時又重複執行了這些比較操作。 
     堆排序可通過樹形結構儲存部分比較結果,可減少比較次數。 


八、拓撲排序 
例 :學生選修課排課先後順序 
拓撲排序:把有向圖中各頂點按照它們相互之間的優先關係排列成一個線性序列的過程。 
方法: 
在有向圖中選一個沒有前驅的頂點且輸出 
從圖中刪除該頂點和所有以它為尾的弧 
重複上述兩步,直至全部頂點均已輸出(拓撲排序成功),或者當圖中不存在無前驅的頂點(圖中有迴路)為止。 
---------------------------------------Code-------------------------------------- 
void TopologicalSort()/*輸出拓撲排序函式。若G無迴路,則輸出G的頂點的一個拓撲序列並返回OK,否則返回ERROR*/ 

      int indegree[M]; 
      int i,k,j; 
      char n; 
      int count=0; 
      Stack thestack; 
      FindInDegree(G,indegree);//對各頂點求入度indegree[0....num] 
      InitStack(thestack);//初始化棧 
      for(i=0;i<G.num;i++) 
          Console.WriteLine("結點"+G.vertices[i].data+"的入度為"+indegree[i]); 
      for(i=0;i<G.num;i++) 
      { 
           if(indegree[i]==0) 
              Push(thestack.vertices[i]); 
      } 
      Console.Write("拓撲排序輸出順序為:"); 
      while(thestack.Peek()!=null) 
      { 
               Pop(thestack.Peek()); 
               j=locatevex(G,n); 
               if (j==-2) 
                  { 
                         Console.WriteLine("發生錯誤,程式結束。"); 
                         exit(); 
                  } 
                Console.Write(G.vertices[j].data); 
                count++; 
                for(p=G.vertices[j].firstarc;p!=NULL;p=p.nextarc) 
                { 
                     k=p.adjvex; 
                     if (!(--indegree[k])) 
                         Push(G.vertices[k]); 
                } 
      } 
      if (count<G.num) 
          Cosole.WriteLine("該圖有環,出現錯誤,無法排序。"); 
      else
          Console.WriteLine("排序成功。"); 

----------------------------------------Code-------------------------------------- 
演算法的時間複雜度O(n+e)。