各種排序算法比較


1.穩定性比較

插入排序、冒泡排序、二叉樹排序、二路歸並排序及其他線形排序是穩定的

選擇排序、希爾排序、快速排序、堆排序是不穩定的

2.時間復雜性比較 

     平均情況  最好情況 最壞情況
歸並排序 O(nlogn)  O(nlogn) O(nlogn)
基數排序 O(n) O(n) O(n)
快速排序 O(nlogn) O(nlogn) O(n2)
希爾排序 O(n1.5) O(n) O(n1.5)
插入排序 O(n2) O(n) O(n2)

選擇排序

O(n2) O(n2) O(n2)

 

 

 

 

 

 

 

3.輔助空間的比較

線形排序、二路歸並排序的輔助空間為O(n),其它排序的輔助空間為O(1);

4.其它比較

插入、冒泡排序的速度較慢,但參加排序的序列局部或整體有序時,這種排序能達到較快的速度。

反而在這種情況下,快速排序反而慢了。

當n較小時,對穩定性不作要求時宜用選擇排序,對穩定性有要求時宜用插入或冒泡排序。

若待排序的記錄的關鍵字在一個明顯有限范圍內時,且空間允許是用桶排序。

當n較大時,關鍵字元素比較隨機,對穩定性沒要求宜用快速排序。

當n較大時,關鍵字元素可能出現本身是有序的,對穩定性有要求時,空間允許的情況下。

宜用歸並排序。

當n較大時,關鍵字元素可能出現本身是有序的,對穩定性沒有要求時宜用堆排序。

*************************************************************************************

重溫經典排序思想--C語言常用排序全解

/*
=============================================================================
相關知識介紹(所有定義只為幫助讀者理解相關概念,並非嚴格定義):
1、穩定排序和非穩定排序

簡單地說就是所有相等的數經過某種排序方法后,仍能保持它們在排序之前的相對次序,我們就
說這種排序方法是穩定的。反之,就是非穩定的。
比如:一組數排序前是a1,a2,a3,a4,a5,其中a2=a4,經過某種排序后為a1,a2,a4,a3,a5,
則我們說這種排序是穩定的,因為a2排序前在a4的前面,排序后它還是在a4的前面。假如變成a1,a4,
a2,a3,a5就不是穩定的了。

2、內排序和外排序

在排序過程中,所有需要排序的數都在內存,並在內存中調整它們的存儲順序,稱為內排序;
在排序過程中,只有部分數被調入內存,並借助內存調整數在外存中的存放順序排序方法稱為外排序。

3、算法的時間復雜度和空間復雜度

所謂算法的時間復雜度,是指執行算法所需要的計算工作量。
一個算法的空間復雜度,一般是指執行這個算法所需要的內存空間。
================================================================================
*/
/*
================================================
功能:選擇排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:

在要排序的一組數中,選出最小的一個數與第一個位置的數交換;
然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環
到倒數第二個數和最后一個數比較為止。

選擇排序是不穩定的。算法復雜度O(n2)--[n的平方]
=====================================================
*/
void select_sort(int *x, int n)
{
int i, j, min, t;

for (i=0; i<n-1; i++) /*要選擇的次數:0~n-2共n-1次*/
{
   min = i; /*假設當前下標為i的數最小,比較后再調整*/
   for (j=i+1; j<n; j++)/*循環找出最小的數的下標是哪個*/
   {
    if (*(x+j) < *(x+min))
    {   
     min = j; /*如果后面的數比前面的小,則記下它的下標*/
    }
   }  
  
   if (min != i) /*如果min在循環中改變了,就需要交換數據*/
   {
    t = *(x+i);
    *(x+i) = *(x+min);
    *(x+min) = t;
   }
}
}

/*
================================================
功能:直接插入排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:

在要排序的一組數中,假設前面(n-1) [n>=2] 個數已經是排
好順序的,現在要把第n個數插到前面的有序數中,使得這n個數
也是排好順序的。如此反復循環,直到全部排好順序。

直接插入排序是穩定的。算法時間復雜度O(n2)--[n的平方]
=====================================================
*/
void insert_sort(int *x, int n)
{
int i, j, t;

for (i=1; i<n; i++) /*要選擇的次數:1~n-1共n-1次*/
{
   /*
    暫存下標為i的數。注意:下標從1開始,原因就是開始時
    第一個數即下標為0的數,前面沒有任何數,單單一個,認為
    它是排好順序的。
   */
   t=*(x+i);
   for (j=i-1; j>=0 && t<*(x+j); j--) /*注意:j=i-1,j--,這里就是下標為i的數,在它前面有序列中找插入位置。*/
   {
    *(x+j+1) = *(x+j); /*如果滿足條件就往后挪。最壞的情況就是t比下標為0的數都小,它要放在最前面,j==-1,退出循環*/
   }

   *(x+j+1) = t; /*找到下標為i的數的放置位置*/
}
}

/*
================================================
功能:冒泡排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:

在要排序的一組數中,對當前還未排好序的范圍內的全部數,自上
而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較
小的往上冒。即:每當兩相鄰的數比較后發現它們的排序與排序要
求相反時,就將它們互換。

下面是一種改進的冒泡算法,它記錄了每一遍掃描后最后下沉數的
位置k,這樣可以減少外層循環掃描的次數。

冒泡排序是穩定的。算法時間復雜度O(n2)--[n的平方]
=====================================================
*/

void bubble_sort(int *x, int n)
{
int j, k, h, t;
  
for (h=n-1; h>0; h=k) /*循環到沒有比較范圍*/
{
   for (j=0, k=0; j<h; j++) /*每次預置k=0,循環掃描后更新k*/
   {
    if (*(x+j) > *(x+j+1)) /*大的放在后面,小的放到前面*/
    {
     t = *(x+j);
     *(x+j) = *(x+j+1);
     *(x+j+1) = t; /*完成交換*/
     k = j; /*保存最后下沉的位置。這樣k后面的都是排序排好了的。*/
    }
   }
}
}

/*
================================================
功能:希爾排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:

在直接插入排序算法中,每次插入一個數,使有序序列只增加1個節點,
並且對插入下一個數沒有提供任何幫助。如果比較相隔較遠距離(稱為
增量)的數,使得數移動時能跨過多個元素,則進行一次比較就可能消除
多個元素交換。D.L.shell於1959年在以他名字命名的排序算法中實現
了這一思想。算法先將要排序的一組數按某個增量d分成若干組,每組中
記錄的下標相差d.對每組中全部元素進行排序,然后再用一個較小的增量
對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成
一組,排序完成。

下面的函數是一個希爾排序算法的一個實現,初次取序列的一半為增量,
以后每次減半,直到增量為1。

希爾排序是不穩定的。
=====================================================
*/
void shell_sort(int *x, int n)
{
int h, j, k, t;

for (h=n/2; h>0; h=h/2) /*控制增量*/
{
   for (j=h; j<n; j++) /*這個實際上就是上面的直接插入排序*/
   {
    t = *(x+j);
    for (k=j-h; (k>=0 && t<*(x+k)); k-=h)
    {
     *(x+k+h) = *(x+k);
    }
    *(x+k+h) = t;
   }
}
}

/*
================================================
功能:快速排序
輸入:數組名稱(也就是數組首地址)、數組中起止元素的下標
================================================
*/
/*
====================================================
算法思想簡單描述:

快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟
掃描后,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次
掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只
減少1。快速排序通過一趟掃描,就能確保某個數(以它為基准點吧)
的左邊各數都比它小,右邊各數都比它大。然后又用同樣的方法處理
它左右兩邊的數,直到基准點的左右只有一個元素為止。它是由
C.A.R.Hoare於1962年提出的。

顯然快速排序可以用遞歸實現,當然也可以用棧化解遞歸實現。下面的
函數是用遞歸實現的,有興趣的朋友可以改成非遞歸的。

快速排序是不穩定的。最理想情況算法時間復雜度O(nlog2n),最壞O(n2)

=====================================================
*/
void quick_sort(int *x, int low, int high)
{
int i, j, t;

if (low < high) /*要排序的元素起止下標,保證小的放在左邊,大的放在右邊。這里以下標為low的元素為基准點*/
{
   i = low;
   j = high;
   t = *(x+low); /*暫存基准點的數*/

   while (i<j) /*循環掃描*/
   {
    while (i<j && *(x+j)>t) /*在右邊的只要比基准點大仍放在右邊*/
    {
     j--; /*前移一個位置*/
    }

    if (i<j) 
    {
     *(x+i) = *(x+j); /*上面的循環退出:即出現比基准點小的數,替換基准點的數*/
     i++; /*后移一個位置,並以此為基准點*/
    }

    while (i<j && *(x+i)<=t) /*在左邊的只要小於等於基准點仍放在左邊*/
    {
     i++; /*后移一個位置*/
    }

    if (i<j)
    {
     *(x+j) = *(x+i); /*上面的循環退出:即出現比基准點大的數,放到右邊*/
     j--; /*前移一個位置*/
    }
   }

   *(x+i) = t; /*一遍掃描完后,放到適當位置*/
   quick_sort(x,low,i-1);   /*對基准點左邊的數再執行快速排序*/
   quick_sort(x,i+1,high);   /*對基准點右邊的數再執行快速排序*/
}
}

/*
================================================
功能:堆排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
================================================
*/
/*
====================================================
算法思想簡單描述:

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。
堆的定義如下:具有n個元素的序列(h1,h2,...,hn),當且僅當
滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2)
時稱之為堆。在這里只討論滿足前者條件的堆。

由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項。完全二叉樹可以
很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。
初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲順序,
使之成為一個堆,這時堆的根節點的數最大。然后將根節點與堆的最后一個節點
交換。然后對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點
的堆,並對它們作交換,最后得到有n個節點的有序序列。

從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素
交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反復調用滲透函數
實現排序的函數。

堆排序是不穩定的。算法時間復雜度O(nlog2n)。

*/
/*
功能:滲透建堆
輸入:數組名稱(也就是數組首地址)、參與建堆元素的個數、從第幾個元素開始
*/
void sift(int *x, int n, int s)
{
int t, k, j;

t = *(x+s); /*暫存開始元素*/
k = s;   /*開始元素下標*/
j = 2*k + 1; /*右子樹元素下標*/

while (j<n)
{
   if (j<n-1 && *(x+j) < *(x+j+1))/*判斷是否滿足堆的條件:滿足就繼續下一輪比較,否則調整。*/
   {
    j++;
   }

   if (t<*(x+j)) /*調整*/
   {
    *(x+k) = *(x+j);
    k = j; /*調整后,開始元素也隨之調整*/
    j = 2*k + 1;
   }
   else /*沒有需要調整了,已經是個堆了,退出循環。*/
   {
    break;
   }
}

*(x+k) = t; /*開始元素放到它正確位置*/
}


/*
功能:堆排序
輸入:數組名稱(也就是數組首地址)、數組中元素個數
*/
void heap_sort(int *x, int n)
{
int i, k, t;
int *p;

for (i=n/2-1; i>=0; i--)
{
   sift(x,n,i); /*初始建堆*/
}

for (k=n-1; k>=1; k--)
{
   t = *(x+0); /*堆頂放到最后*/
   *(x+0) = *(x+k);
   *(x+k) = t;
   sift(x,k,0); /*剩下的數再建堆*/ 
}
}


void main()

#define MAX 4
int *p, i, a[MAX];

/*錄入測試數據*/
p = a;
printf("Input %d number for sorting :/n",MAX);
for (i=0; i<MAX; i++)
{
   scanf("%d",p++);
}
printf("/n");

/*測試選擇排序*/


p = a;
select_sort(p,MAX);
/**/


/*測試直接插入排序*/

/*
p = a;
insert_sort(p,MAX);
*/


/*測試冒泡排序*/

/*
p = a;
insert_sort(p,MAX);
*/

/*測試快速排序*/

/*
p = a;
quick_sort(p,0,MAX-1);
*/

/*測試堆排序*/

/*
p = a;
heap_sort(p,MAX);
*/

for (p=a, i=0; i<MAX; i++)
{
   printf("%d ",*p++);
}

printf("/n");
system("pause");
}

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/TrueLie/archive/2007/07/12/1687466.aspx

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM