幾種排序演算法介紹與效能分析
本文以對整形陣列升序排序為例,列舉了排序的幾種演算法及相應的Java實現,並在本文最後給出這幾種演算法的效能分析圖表。
1、插入排序
基本思路:在每次迴圈中把一個元素插入到已經排序的部分序列裡的合適位置,使得到的序列仍然是有序的。
實現:
void sort(int a[]) throws Exception {
int tmp;
int j;
for (int i = 1; i <= a.length; i++) {
tmp = a;
for (j = i - 1; j >= 0 && a[j] > tmp; j--) {
a[j + 1] = a[j];
}
a[j + 1] = tmp;
}
}
2、希爾排序
基本思路:任取一個小於n的整數S1作為增量,把所有元素分成S1個組。所有間距為S1的元素放在同一個組中。
第一組:{A[1],A[S1+1],A[2*S1+1],……}
第二組:{A[2],A[S1+2],A[2*S1+2],……}
第三組:{A[3],A[S1+3],A[2*S1+3],……}
……
第s1組:{A[S1],A[2*S1],A[3*S1],……}
先在各組內進行直接插人排序;然後,取第二個增量S2(<S1)重複上述的分組和排序,直至所取的增量St=1(St<St-1<St-2<…<S2<S1),即所有記錄放在同一組中進行直接插入排序為止。
實現:
void sort(int a[]) throws Exception {
int h[] = { 109, 41, 19, 5, 1 };
int tmp, j;
int incno;
for (tmp = 0; h[tmp] > a.length; tmp++);
for (incno = tmp; incno <= h.length - 1; incno++) {
for (int i = h[incno]; i <= a.length - 1; i++) {
tmp = a;
for (j = i - h[incno]; j >= 0 && a[j] > tmp; j = j - h[incno]) {
a[j + h[incno]] = a[j];
}
a[j + h[incno]] = tmp;
}
}
}
3、氣泡排序
基本思路:兩兩比較待排序的資料,發現兩個資料的次序相反則進行交換,直到沒有反序的資料為止。
實現:
void sort(int a[]) throws Exception {
for (int i = a.length; --i >= 0;){
for (int j = 0; j < i; j++) {
if (a[j] > a[j + 1]) {
int T = a[j];
a[j] = a[j + 1];
a[j + 1] = T;
}
}
}
}
4、選擇排序
基本思路:從最後一個元素開始,從待排序的資料中選出記錄最大元素位置,在將該元素同未排好順序數列的最後一個元素交換位置,迴圈這個操作,直到全部資料排序完畢。
實現:
void sort(int a[]) throws Exception {
for (int i = a.length; --i >= 0;){
int largest=0;
for (int j = 1; j <= i; j++) {
if (a[largest] < a[j]) {
largest = j;
}
}
if (largest != i) {
int T = a[largest];
a[largest] = a;
a = T;
}
}
}
5、快速排序
基本思路:在A[1..n]中任取一個數據元素作為比較的“基準”(不妨記為X),將資料區劃分為左右兩個部分:A[1..i-1]和A[i+1..n],且A[1..i-1]≤X≤A[i+1..n](1≤i≤n),當A[1..i-1]和A[i+1..n]非空時,分別對它們進行上述的劃分過程,直至所有資料元素均已排序為止。這個“基準”本實現採用數列中值
實現:
void quickSort(int a[], int low, int hig) throws Exception
{
int lo = low;
int hi = hig;
int mid;
if ( hig > low)
{
mid = a[ ( log + hiw ) / 2 ];
while( lo <= hi )
{
while( ( lo < hig ) && ( a[lo] < mid ) )
++lo;
while( ( hi > low ) && ( a[hi] > mid ) )
--hi;
if( lo <= hi )
{
swap(a, lo, hi);
++lo;
--hi;
}
}
if( low < hi )
quickSort( a, low, hi );
if( lo < hig )
quickSort( a, lo, hiw );
}
}
private void swap(int a[], int i, int j)
{
int T;
T = a;
a = a[j];
a[j] = T;
}
public void sort(int a[]) throws Exception
{
quickSort(a, 0, a.length - 1);
}
6、堆排序
基本思路:堆排序是一種樹形選擇排序,在排序過程中,將A[n]看成是完全二叉樹的順序儲存結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係來選擇最小的元素。
實現:
void sort(int a[]) throws Exception {
int i;
for (i = a.length / 2; i >= 0; i--) {
perc_down(i, a, a.length - 1);
}
for (i = a.length - 1; i >= 0; i--) {
delete_max(i, a);
}
}
void delete_max(int ix, int a[]) throws Exception {
int ret;
ret = a[0];
a[0] = a[ix];
perc_down(0, a, ix - 1);
a[ix] = ret;
}
void perc_down(int ix, int a[], int lng) throws Exception {
int i, tmp;
tmp = a[ix];
i = ix;
while (i * 2 + 1 <= lng) {
if (i * 2 + 1 == lng || a[i * 2 + 1] > a[i * 2 + 2]) {
if (a[i * 2 + 1] < tmp)
break;
a = a[i * 2 + 1];
i = i * 2 + 1;
} else {
if (a[i * 2 + 2] < tmp)
break;
a = a[i * 2 + 2];
i = i * 2 + 2;
}
}
a = tmp;
}
7、歸併排序
基本思路:設有兩個有序(升序)序列儲存在同一陣列中相鄰的位置上,不妨設為A[l..m],A[m+1..h],將它們歸併為一個有序數列,並存儲在A[l..h]。
為了減少資料移動次數,不妨採用一個臨時工作陣列C,將中間排序結果暫時儲存在C陣列中,等歸併結束後,再將C陣列值複製給A。
實現:
void sort(int a[]) throws Exception {
mergesort(a, 0, a.length - 1);
}
private void merge(int a[], int begin, int m, int end) throws Exception {
int i = begin, j = m + 1, k = 0;
int[] tmp = new int[end - begin + 1];
while (i <= m && j <= end) {
if (a > a[j]) {
tmp[k++] = a[j++];
} else {
tmp[k++] = a[i++];
}
}
if (i == m + 1) {
for (; j <= end; j++) {
tmp[k++] = a[j];
}
} else {
for (; i <= m; i++) {
tmp[k++] = a;
}
}
for (k = 0; k < tmp.length; k++) {
a[begin + k] = tmp[k];
}
}
private void mergesort(int a[], int begin, int end) throws Exception {
if (end - begin > 0) {
int m = (end - begin) / 2;
mergesort(a, begin, begin + m);
mergesort(a, begin + m + 1, end);
merge(a, begin, begin + m, end);
}
}
效能比較
時間複雜度
空間複雜度
穩定
1
插入排序
O(n2)
1
√
2
希爾排序
O(n2)
1
×
3
氣泡排序
O(n2)
1
√
4
選擇排序
O(n2)
1
×
5
快速排序
O(Nlogn)
O(logn)
×
6
堆排序
O(Nlogn)
1
×
7
歸併排序
O(Nlogn)
O(n)
√