八種排序演算法總結之
c++版本
五種簡單排序演算法
一、 氣泡排序 【穩定的】
void bubblesort( int* a,int count ) //
實現從小到大的最終結果
現在注意,我們給出o方法的定義:
若存在一常量k和起點n0,使當n>=n0時,有f(n)<=k*g(n),則f(n) = o(g(n))。(呵呵,不要說沒學好數學呀,對於程式設計數學是非常重要的!!!)
現在我們來看1/2*(n-1)*n,當k=1/2,n0=1,g(n)=n*n時,1/2*(n-1)*n<=1/2*n*n=k*g(n)。所以f(n) =o(g(n))=o(n*n)。所以我們程式迴圈的複雜度為o(n*n)。
二、 交換排序 【穩定的】
void exchangesort( int *a,int count)
時間複雜度為o(n*n)。
三、 選擇法 【不穩定的】
void selectsort( int *a,int count)
a[pos] = a[i];
a[i] = temp;
}遺憾的是演算法需要的迴圈次數依然是1/2*(n-1)*n。所以演算法複雜度為o(n*n)。
我們來看他的交換。由於每次外層迴圈只產生一次交換(只有乙個最小值)。所以f(n)<=n
所以我們有f(n)=o(n)。所以,在資料較亂的時候,可以減少一定的交換次數。
四、 插入法 【穩定的】
void insertsort( int *a,int count)
a[pos+1] = temp;
}其複雜度仍為o(n*n)。
最終,我個人認為,在簡單排序演算法中,直接插入排序是最好的。
五、 希爾排序法 【不穩定的】
/**
希爾排序,n為陣列的個數*/
void shellsort( int arr, int n )
arr[ pos + d] = temp;
}} while( d > 1 );}
三種高階排序演算法
一、 快速排序 輔助空間複雜度為o(1) 【不穩定的】
void quicksort( int *a,int left, int right)
} while ( i//
如果兩邊的下標交錯,就停止(完成一次)
//當左半邊有值(left),遞迴左半邊
if( left < j )
quicksort( a,left, j);
//當右半邊有值(right>i),遞迴右半邊
if( i < right )
quicksort( a, i,right);
它的工作看起來象乙個二叉樹。首先我們選擇乙個中間值middle,程式中我們使用陣列中間值,然後把比它小的放在左邊,大的放在右邊(具體的實現是從兩邊找,找到一對後交換)。然後對兩邊分別使用這個過程(最容易的方法——遞迴)。注意,由於資料的隨機性,對middle的選擇並不會影響該演算法的效率。
注意,在掃瞄過程中,對於給定參考值,對於向右(左)掃瞄,如果掃瞄值大(小)於或等於參考值,就需要進行交換。最終得到的結果是,
j左邊的值都小於參考值,而
i右邊的值都大於參考值,j和
i之間的值都等於參考值。對j左邊和i右邊的分別使用遞迴,就可以完成最終的排序。
這裡我沒有給出行為的分析,因為這個很簡單,我們直接來分析演算法:首先我們考慮最理想的情況
1.陣列的大小是2的冪,這樣分下去始終可以被2整除。假設為2的k次方,即k=log2(n)。
2.每次我們選擇的值剛好是中間值,這樣,陣列才可以被等分。
第一層遞迴,迴圈n次,第二層迴圈2*(n/2)......
所以共有n+2(n/2)+4(n/4)+...+n*(n/n)= n+n+n+...+n=k*n=log2(n)*n
所以演算法複雜度為o(log2(n)*n)
其他的情況只會比這種情況差,最差的情況是每次選擇到的middle都是最小值或最大值,那麼他將變
成交換法(由於使用了遞迴,情況更糟),但是糟糕的情況只會持續乙個流程,到下乙個流程的時候就很可能已經避開了該中間的最大和最小值,因為陣列下標變化了,於是中間值不在是那個最大或者最小值。但是你認為這種情況發生的機率有多大??呵呵,你完全不必擔心這個問題。實踐證明,大多數的情況,快速排序總是最好的。
如果你擔心這個問題,你可以使用堆排序,這是一種穩定的o(log2(n)*n)演算法,但是通常情況下速度要慢
於快速排序(因為要重組堆)。
二、 歸併排序(兩種實現方法均要掌握) 【穩定的】
歸併排序是一種極好的外部排序方法,即針對資料儲存在磁碟上而不是高速記憶體中的問題。
//以下程式參考資料結構課本p286頁的模板,為使用指標鍊錶實現的
#include
using
namespace std;
struct node;
node * divide_from( node * head )
}second_half = midpoint->next;
midpoint->next =null; //
在這裡將原鏈拆斷,分為兩段
return second_half;
}node * merge( node * first, node * second)
else
}if( first==null )
last_sorted->next= second;
else
last_sorted->next= first;
return combined.next; //
返回啞節點的後繼指標,即為合併後的鍊錶的頭指標 }
//這裡的引數必須是引用呼叫,需要這個指引去允許函式修改呼叫自變數
void mergesort( node * &head)
}int main()
cout//呼叫歸併排序後的結果
head = p1;
while( head != null )
cout<//以下程式為使用陣列實現的歸併排序,輔助空間複雜度為o(n)
#include
using
namespace std;
void merge( int data, int left, int mid, int right )
else
}if( i//
左邊的陣列尚未取盡
for( j=i; j < n1; j++,k++)
data[k] =l[j];
else
//if( j//
右邊的陣列尚未取盡 ,
這句話可要可不要
for( i=j; idata[k] =r[i];
delete l; //
**記憶體
delete r;}/*
* left:陣列的開始下標,一般為0;right:陣列的結束下標,一般為 (n-1)
*/void mergesort( int data, int left, int right )
}int main();//
排序前的輸出
for(int i=0; i<9;i++)
cout<" ";
cout//排序後的輸出
for(int i=0; i<9;i++)
cout<" ";
cout<
三、 堆排序 【不穩定的】
/*
*
向堆中插入current元素的函式*/
void insert_heap( int data,const int ¤t, intlow, int high )
}data[ low ] =current;}/*
*
建立堆函式,num為陣列data的元素個數
*
只有乙個結點的<2-樹》自動滿足堆的屬性,因此不必擔心樹中的任何樹葉,即
*
不必擔心表的後一半中的元素。如果從表的中間點開始並從後向前工作,就
*
能夠使用函式insert_heap去將每個元素插入到包含了所有後面元素的部分堆
*
中,從而建立完整的堆。 */
void build_heap( int data, int num ) }/*
*
堆排序主函式,num為陣列data的元素個數*/
void heap_sort( int data, int num )
}int main()
;for(int i=0; i< sizeof(a)/sizeof(int); i++)
cout//呼叫堆排序
for(int i=0; i< sizeof(a)/sizeof(int); i++)
cout 插入排序 1.直接插入排序 原理 將陣列分為無序區和有序區兩個區,然後不斷將無序區的第乙個元素按大小順序插入到有序區中去,最終將所有無序區元素都移動到有序區完成排序。要點 設立哨兵,作為臨時儲存和判斷陣列邊界之用。實現 void insertsort node l,int length void s... 實現中,使用vector作為桶,只是從編碼便利的角度出發。因為需要對各個桶進行單獨排序,所以,對桶進行的排序演算法是否穩定,影響了整個演算法的穩定性,這裡使用的是sort演算法,因此整個演算法就是不穩定的。void bucketsort int pdata,int size 建立桶 int buck... include include 氣泡排序 void boblesort int arr,int n 插入排序 void insertsort2 int arr,int n 希爾排序 void shellsort2 int arr,int n 選擇排序 void selectsort int arr,...八種排序演算法總結
排序演算法之八 桶排序 C 版本
八種排序演算法