各類排序算法比較

1.穩定性比較算法

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

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

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.輔助空間的比較ui

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

4.其它比較.net

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

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

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

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

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

當n較大時,關鍵字元素可能出現自己是有序的,對穩定性有要求時,空間容許的狀況下。

宜用歸併排序。

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

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

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

/*
=============================================================================
相關知識介紹(全部定義只爲幫助讀者理解相關概念,並不是嚴格定義):
一、穩定排序和非穩定排序

簡單地說就是全部相等的數通過某種排序方法後,仍能保持它們在排序以前的相對次序,咱們就
說這種排序方法是穩定的。反之,就是非穩定的。
好比:一組數排序前是a1,a2,a3,a4,a5,其中a2=a4,通過某種排序後爲a1,a2,a4,a3,a5,
則咱們說這種排序是穩定的,由於a2排序前在a4的前面,排序後它仍是在a4的前面。假如變成a1,a4,
a2,a3,a5就不是穩定的了。

二、內排序和外排序

在排序過程當中,全部須要排序的數都在內存,並在內存中調整它們的存儲順序,稱爲內排序;
在排序過程當中,只有部分數被調入內存,並藉助內存調整數在外存中的存放順序排序方法稱爲外排序。

三、算法的時間複雜度和空間複雜度

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

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

選擇排序是不穩定的。算法複雜度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

相關文章
相關標籤/搜索