『壹』 3. 用任意一種編程語言(C/C++/java/C#/VB.NET)寫出任意一種你所知的排序演算法(比如:冒泡排序, 歸並排
C語言寫的 快排
#include <stdio.h>
#include <stdlib.h>
void sort(int l, int r, int *a)
{ int i, j, x;
x=a[l];
i=l;
j=r;
while(i<j){
while(i<j&&a[j]>=x) j--;
if(i<j) {swap(a+i,a+j); i++;}
while(i<j&&a[i]<=x) i++;
if(i<j) {swap(a+i,a+j); j--;}
}
if(l<r) {sort(l, i-1, a+l);
sort(i+1, r, a+i+1);}
}
void press(int n, int *a)
{ int i;
for(i=0;i<n;i++) printf("%d ", a[i]);
}
main()
{ int n;
int *a, i, j;
scanf("%d", &n);
a = (int*)malloc(sizeof(int)*(n));
for(i=0;i<n;i++) scanf("%d", a+i);
sort(0, n, a);
press(n, a);
free(a);
}
『貳』 常見的排序演算法—選擇,冒泡,插入,快速,歸並
太久沒看代碼了,最近打算復習一下java,又突然想到了排序演算法,就把幾種常見的排序演算法用java敲了一遍,這里統一將無序的序列從小到大排列。
選擇排序是一種簡單直觀的排序演算法。它的工作原理是:第一次從待排序的數據元素中選出最小的一個元素,存放在序列的起始位置,然後再從剩餘的未排序元素中尋找到最小元素,繼續放在下一個位置,直到待排序元素個數為0。
選擇排序代碼如下:
public void Select_sort(int[] arr) {
int temp,index;
for( int i=0;i<10;i++) {
index = i;
for(int j = i + 1 ; j < 10 ; j++) {
if(arr[j] < arr[index])
index = j;
}
/*
temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
*/
swap(arr,i,index);
}
System.out.print("經過選擇排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr[i] +" ");
System.out.println("");
}
冒泡排序是一種比較基礎的排序演算法,其思想是相鄰的元素兩兩比較,較大的元素放後面,較小的元素放前面,這樣一次循環下來,最大元素就會歸位,若數組中元素個數為n,則經過(n-1)次後,所有元素就依次從小到大排好序了。整個過程如同氣泡冒起,因此被稱作冒泡排序。
選擇排序代碼如下:
public void Bubble_sort(int[] arr) {
int temp;
for(int i = 0 ; i < 9 ; i++) {
for(int j = 0 ; j < 10 - i - 1 ;j++) {
if(arr[j] > arr[j+1]) {
/*
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
*/
swap(arr,j,j+1);
}
}
}
System.out.print("經過冒泡排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr[i] +" ");
System.out.println("");
}
插入排序也是一種常見的排序演算法,插入排序的思想是:創建一個與待排序數組等大的數組,每次取出一個待排序數組中的元素,然後將其插入到新數組中合適的位置,使新數組中的元素保持從小到大的順序。
插入排序代碼如下:
public void Insert_sort(int[] arr) {
int length = arr.length;
int[] arr_sort = new int[length];
int count = 0;
for(int i = 0;i < length; i++) {
if(count == 0) {
arr_sort[0] = arr[0];
}else if(arr[i] >= arr_sort[count - 1]) {
arr_sort[count] = arr[i];
}else if(arr[i] < arr_sort[0]) {
insert(arr,arr_sort,arr[i],0,count);
}else {
for(int j = 0;j < count - 1; j++) {
if(arr[i] >= arr_sort[j] && arr[i] < arr_sort[j+1]) {
insert(arr,arr_sort,arr[i],j+1,count);
break;
}
}
}
count++;
}
System.out.print("經過插入排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr_sort[i] +" ");
System.out.println("");
}
public void insert(int[] arr,int[] arr_sort,int value,int index,int count) {
for(int i = count; i > index; i--)
arr_sort[i] = arr_sort[i-1];
arr_sort[index] = value;
}
快速排序的效率比冒泡排序演算法有大幅提升。因為使用冒泡排序時,一次外循環只能歸位一個值,有n個元素最多就要執行(n-1)次外循環。而使用快速排序時,一次可以將所有元素按大小分成兩堆,也就是平均情況下需要logn輪就可以完成排序。
快速排序的思想是:每趟排序時選出一個基準值(這里以首元素為基準值),然後將所有元素與該基準值比較,並按大小分成左右兩堆,然後遞歸執行該過程,直到所有元素都完成排序。
public void Quick_sort(int[] arr, int left, int right) {
if(left >= right)
return ;
int temp,t;
int j = right;
int i = left;
temp = arr[left];
while(i < j) {
while(arr[j] >= temp && i < j)
j--;
while(arr[i] <= temp && i < j)
i++;
if(i < j) {
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[left] = arr[i];
arr[i] = temp;
Quick_sort(arr,left, i - 1);
Quick_sort(arr, i + 1, right);
}
歸並排序是建立在歸並操作上的一種有效的排序演算法,歸並排序對序列的元素進行逐層折半分組,然後從最小分組開始比較排序,每兩個小分組合並成一個大的分組,逐層進行,最終所有的元素都是有序的。
public void Mergesort(int[] arr,int left,int right) {
if(right - left > 0) {
int[] arr_1 = new int[(right - left)/2 + 1];
int[] arr_2 = new int[(right - left + 1)/2];
int j = 0;
int k = 0;
for(int i = left;i <= right;i++) {
if(i <= (right + left)/2) {
arr_1[j++] = arr[i];
}else {
arr_2[k++] = arr[i];
}
}
Mergesort(arr_1,0,(right - left)/2);
Mergesort(arr_2,0,(right - left - 1)/2);
Merge(arr_1,arr_2,arr);
}
}
public void Merge(int[] arr_1,int[] arr_2,int[] arr) {
int i = 0;
int j = 0;
int k = 0;
int L1 = arr_1.length;
int L2 = arr_2.length;
while(i < L1 && j < L2) {
if(arr_1[i] <= arr_2[j]) {
arr[k] = arr_1[i];
i++;
}else {
arr[k] = arr_2[j];
j++;
}
k++;
}
if(i == L1) {
for(int t = j;j < L2;j++)
arr[k++] = arr_2[j];
}else {
for(int t = i;i < L1;i++)
arr[k++] = arr_1[i];
}
}
歸並排序這里我使用了left,right等變數,使其可以通用,並沒有直接用數字表示那麼明確,所以給出相關偽代碼,便於理解。
Mergesort(arr[0...n-1])
//輸入:一個可排序數組arr[0...n-1]
//輸出:非降序排列的數組arr[0...n-1]
if n>1
arr[0...n/2-1] to arr_1[0...(n+1)/2-1]//確保arr_1中元素個數>=arr_2中元素個數
//對於總個數為奇數時,arr_1比arr_2中元素多一個;對於總個數為偶數時,沒有影響
arr[n/2...n-1] to arr_2[0...n/2-1]
Mergesort(arr_1[0...(n+1)/2-1])
Mergesort(arr_2[0...n/2-1])
Merge(arr_1,arr_2,arr)
Merge(arr_1[0...p-1],arr_2[0...q-1],arr[0...p+q-1])
//輸入:兩個有序數組arr_1[0...p-1]和arr_2[0...q-1]
//輸出:將arr_1與arr_2兩數組合並到arr
int i<-0;j<-0;k<-0
while i
<p span="" do<="" jif arr_1[i] <= arr_2[j]
arr[k] <- arr_1[i]
i<-i+1
else arr[k] <- arr_2[j];j<-j+1
k<-k+1
if i=p
arr_2[j...q-1] to arr[k...p+q-1]
else arr_1[i...p-1] to arr[k...p+q-1]
package test_1;
import java.util.Scanner;
public class Test01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] arr_1 = new int[10];
for(int i = 0 ; i < 10 ; i++)
arr_1[i] = sc.nextInt();
Sort demo_1 = new Sort();
//1~5一次只能運行一個,若多個同時運行,則只有第一個有效,後面幾個是無效排序。因為第一個運行的已經將帶排序數組排好序。
demo_1.Select_sort(arr_1);//-----------------------1
//demo_1.Bubble_sort(arr_1);//---------------------2
/* //---------------------3
demo_1.Quick_sort(arr_1, 0 , arr_1.length - 1);
System.out.print("經過快速排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr_1[i] +" ");
System.out.println("");
*/
//demo_1.Insert_sort(arr_1);//--------------------4
/* //--------------------5
demo_1.Mergesort(arr_1,0,arr_1.length - 1);
System.out.print("經過歸並排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr_1[i] +" ");
System.out.println("");
*/
}
}
class Sort {
public void swap(int arr[],int a, int b) {
int t;
t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
public void Select_sort(int[] arr) {
int temp,index;
for( int i=0;i<10;i++) {
index = i;
for(int j = i + 1 ; j < 10 ; j++) {
if(arr[j] < arr[index])
index = j;
}
/*
temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
*/
swap(arr,i,index);
}
System.out.print("經過選擇排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr[i] +" ");
System.out.println("");
}
public void Bubble_sort(int[] arr) {
int temp;
for(int i = 0 ; i < 9 ; i++) {
for(int j = 0 ; j < 10 - i - 1 ;j++) {
if(arr[j] > arr[j+1]) {
/*
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
*/
swap(arr,j,j+1);
}
}
}
System.out.print("經過冒泡排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr[i] +" ");
System.out.println("");
}
public void Quick_sort(int[] arr, int left, int right) {
if(left >= right)
return ;
int temp,t;
int j = right;
int i = left;
temp = arr[left];
while(i < j) {
while(arr[j] >= temp && i < j)
j--;
while(arr[i] <= temp && i < j)
i++;
if(i < j) {
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[left] = arr[i];
arr[i] = temp;
Quick_sort(arr,left, i - 1);
Quick_sort(arr, i + 1, right);
}
public void Insert_sort(int[] arr) {
int length = arr.length;
int[] arr_sort = new int[length];
int count = 0;
for(int i = 0;i < length; i++) {
if(count == 0) {
arr_sort[0] = arr[0];
}else if(arr[i] >= arr_sort[count - 1]) {
arr_sort[count] = arr[i];
}else if(arr[i] < arr_sort[0]) {
insert(arr,arr_sort,arr[i],0,count);
}else {
for(int j = 0;j < count - 1; j++) {
if(arr[i] >= arr_sort[j] && arr[i] < arr_sort[j+1]) {
insert(arr,arr_sort,arr[i],j+1,count);
break;
}
}
}
count++;
}
System.out.print("經過插入排序後:");
for(int i = 0 ; i < 10 ; i++)
System.out.print( arr_sort[i] +" ");
System.out.println("");
}
public void insert(int[] arr,int[] arr_sort,int value,int index,int count) {
for(int i = count; i > index; i--)
arr_sort[i] = arr_sort[i-1];
arr_sort[index] = value;
}
public void Mergesort(int[] arr,int left,int right) {
if(right - left > 0) {
int[] arr_1 = new int[(right - left)/2 + 1];
int[] arr_2 = new int[(right - left + 1)/2];
int j = 0;
int k = 0;
for(int i = left;i <= right;i++) {
if(i <= (right + left)/2) {
arr_1[j++] = arr[i];
}else {
arr_2[k++] = arr[i];
}
}
Mergesort(arr_1,0,(right - left)/2);
Mergesort(arr_2,0,(right - left - 1)/2);
Merge(arr_1,arr_2,arr);
}
}
public void Merge(int[] arr_1,int[] arr_2,int[] arr) {
int i = 0;
int j = 0;
int k = 0;
int L1 = arr_1.length;
int L2 = arr_2.length;
while(i < L1 && j < L2) {
if(arr_1[i] <= arr_2[j]) {
arr[k] = arr_1[i];
i++;
}else {
arr[k] = arr_2[j];
j++;
}
k++;
}
if(i == L1) {
for(int t = j;j < L2;j++)
arr[k++] = arr_2[j];
}else {
for(int t = i;i < L1;i++)
arr[k++] = arr_1[i];
}
}
}
若有錯誤,麻煩指正,不勝感激。
『叄』 快速排序演算法的示例代碼
usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;namespacetest{classQuickSort{staticvoidMain(string[]args){int[]array={49,38,65,97,76,13,27};sort(array,0,array.Length-1);Console.ReadLine();}/**一次排序單元,完成此方法,key左邊都比key小,key右邊都比key大。**@paramarray排序數組**@paramlow排序起始位置**@paramhigh排序結束位置**@return單元排序後的數組*/privatestaticintsortUnit(int[]array,intlow,inthigh){intkey=array[low];while(low<high){/*從後向前搜索比key小的值*/while(array[high]>=key&&high>low)--high;/*比key小的放左邊*/array[low]=array[high];/*從前向後搜索比key大的值,比key大的放右邊*/while(array[low]<=key&&high>low)++low;/*比key大的放右邊*/array[high]=array[low];}/*左邊都比key小,右邊都比key大。//將key放在游標當前位置。//此時low等於high*/array[low]=key;foreach(intiinarray){Console.Write({0} ,i);}Console.WriteLine();returnhigh;}/**快速排序*@paramarry*@return*/publicstaticvoidsort(int[]array,intlow,inthigh){if(low>=high)return;/*完成一次單元排序*/intindex=sortUnit(array,low,high);/*對左邊單元進行排序*/sort(array,low,index-1);/*對右邊單元進行排序*/sort(array,index+1,high);}}}運行結果:27 38 13 49 76 97 65
13 27 38 49 76 97 6513 27 38 49 65 76 97
快速排序就是遞歸調用此過程——在以49為中點分割這個數據序列,分別對前面一部分和後面一部分進行類似的快速排序,從而完成全部數據序列的快速排序,最後把此數據序列變成一個有序的序列,根據這種思想對於上述數組A的快速排序的全過程如圖6所示:
初始狀態 {49 38 65 97 76 13 27} 進行一次快速排序之後劃分為 {27 38 13} 49 {76 97 65} 分別對前後兩部分進行快速排序{27 38 13} 經第三步和第四步交換後變成 {13 27 38} 完成排序。{76 97 65} 經第三步和第四步交換後變成 {65 76 97} 完成排序。圖示 快速排序的最壞情況基於每次劃分對主元的選擇。基本的快速排序選取第一個元素作為主元。這樣在數組已經有序的情況下,每次劃分將得到最壞的結果。一種比較常見的優化方法是隨機化演算法,即隨機選取一個元素作為主元。這種情況下雖然最壞情況仍然是O(n^2),但最壞情況不再依賴於輸入數據,而是由於隨機函數取值不佳。實際上,隨機化快速排序得到理論最壞情況的可能性僅為1/(2^n)。所以隨機化快速排序可以對於絕大多數輸入數據達到O(nlogn)的期望時間復雜度。一位前輩做出了一個精闢的總結:「隨機化快速排序可以滿足一個人一輩子的人品需求。」
隨機化快速排序的唯一缺點在於,一旦輸入數據中有很多的相同數據,隨機化的效果將直接減弱。對於極限情況,即對於n個相同的數排序,隨機化快速排序的時間復雜度將毫無疑問的降低到O(n^2)。解決方法是用一種方法進行掃描,使沒有交換的情況下主元保留在原位置。 QUICKSORT(A,p,r)
1if p<r
2then q ←PARTITION(A,p,r)
3QUICKSORT(A,p,q-1)
4QUICKSORT(A,q+1,r)
為排序一個完整的數組A,最初的調用是QUICKSORT(A,1,length[A])。
快速排序演算法的關鍵是PARTITION過程,它對子數組A[p..r]進行就地重排:
PARTITION(A,p,r)
1x←A[r]
2i←p-1
3for j←p to r-1
4do if A[j]≤x
5then i←i+1
6exchange A[i]←→A[j]
7exchange A[i+1]←→A[r]
8return i+1 對PARTITION和QUICKSORT所作的改動比較小。在新的劃分過程中,我們在真正進行劃分之前實現交換:
(其中PARTITION過程同快速排序偽代碼(非隨機))
RANDOMIZED-PARTITION(A,p,r)
1i← RANDOM(p,r)
2exchange A[r]←→A[i]
3return PARTITION(A,p,r)
新的快速排序過程不再調用PARTITION,而是調用RANDOMIZED-PARTITION。
RANDOMIZED-QUICKSORT(A,p,r)
1if p<r
2then q← RANDOMIZED-PARTITION(A,p,r)
3RANDOMIZED-QUICKSORT(A,p,q-1)
4RANDOMIZED-QUICKSORT(A,q+1,r) 這里為方便起見,我們假設演算法Quick_Sort的范圍閾值為1(即一直將線性表分解到只剩一個元素),這對該演算法復雜性的分析沒有本質的影響。
我們先分析函數partition的性能,該函數對於確定的輸入復雜性是確定的。觀察該函數,我們發現,對於有n個元素的確定輸入L[p..r],該函數運行時間顯然為θ(n)。
最壞情況
無論適用哪一種方法來選擇pivot,由於我們不知道各個元素間的相對大小關系(若知道就已經排好序了),所以我們無法確定pivot的選擇對劃分造成的影響。因此對各種pivot選擇法而言,最壞情況和最好情況都是相同的。
我們從直覺上可以判斷出最壞情況發生在每次劃分過程產生的兩個區間分別包含n-1個元素和1個元素的時候(設輸入的表有n個元素)。下面我們暫時認為該猜測正確,在後文我們再詳細證明該猜測。
對於有n個元素的表L[p..r],由於函數Partition的計算時間為θ(n),所以快速排序在序壞情況下的復雜性有遞歸式如下:
T(1)=θ(1),T(n)=T(n-1)+T(1)+θ(n) (1)
用迭代法可以解出上式的解為T(n)=θ(n2)。
這個最壞情況運行時間與插入排序是一樣的。
下面我們來證明這種每次劃分過程產生的兩個區間分別包含n-1個元素和1個元素的情況就是最壞情況。
設T(n)是過程Quick_Sort作用於規模為n的輸入上的最壞情況的時間,則
T(n)=max(T(q)+T(n-q))+θ(n),其中1≤q≤n-1 (2)
我們假設對於任何k<n,總有T(k)≤ck,其中c為常數;顯然當k=1時是成立的。
將歸納假設代入(2),得到:
T(n)≤max(cq2+c(n-q)2)+θ(n)=c*max(q2+(n-q)2)+θ(n)
因為在[1,n-1]上q2+(n-q)2關於q遞減,所以當q=1時q2+(n-q)2有最大值n2-2(n-1)。於是有:
T(n)≤cn2-2c(n-1)+θ(n)≤cn2
只要c足夠大,上面的第二個小於等於號就可以成立。於是對於所有的n都有T(n)≤cn。
這樣,排序演算法的最壞情況運行時間為θ(n2),且最壞情況發生在每次劃分過程產生的兩個區間分別包含n-1個元素和1個元素的時候。
時間復雜度為o(n2)。
最好情況
如果每次劃分過程產生的區間大小都為n/2,則快速排序法運行就快得多了。這時有:
T(n)=2T(n/2)+θ(n),T(1)=θ(1) (3)
解得:T(n)=θ(nlogn)
快速排序法最佳情況下執行過程的遞歸樹如下圖所示,圖中lgn表示以10為底的對數,而本文中用logn表示以2為底的對數.
由於快速排序法也是基於比較的排序法,其運行時間為Ω(nlogn),所以如果每次劃分過程產生的區間大小都為n/2,則運行時間θ(nlogn)就是最好情況運行時間。
但是,是否一定要每次平均劃分才能達到最好情況呢?要理解這一點就必須理解對稱性是如何在描述運行時間的遞歸式中反映的。我們假設每次劃分過程都產生9:1的劃分,乍一看該劃分很不對稱。我們可以得到遞歸式:
T(n)=T(n/10)+T(9n/10)+θ(n),T(1)=θ(1) (4)
請注意樹的每一層都有代價n,直到在深度log10n=θ(logn)處達到邊界條件,以後各層代價至多為n。遞歸於深度log10/9n=θ(logn)處結束。這樣,快速排序的總時間代價為T(n)=θ(nlogn),從漸進意義上看就和劃分是在中間進行的一樣。事實上,即使是99:1的劃分時間代價也為θ(nlogn)。其原因在於,任何一種按常數比例進行劃分所產生的遞歸樹的深度都為θ(nlogn),其中每一層的代價為O(n),因而不管常數比例是什麼,總的運行時間都為θ(nlogn),只不過其中隱含的常數因子有所不同。(關於演算法復雜性的漸進階,請參閱演算法的復雜性)
平均情況
快速排序的平均運行時間為θ(nlogn)。
我們對平均情況下的性能作直覺上的分析。
要想對快速排序的平均情況有個較為清楚的概念,我們就要對遇到的各種輸入作個假設。通常都假設輸入數據的所有排列都是等可能的。後文中我們要討論這個假設。
當我們對一個隨機的輸入數組應用快速排序時,要想在每一層上都有同樣的劃分是不太可能的。我們所能期望的是某些劃分較對稱,另一些則很不對稱。事實上,我們可以證明,如果選擇L[p..r]的第一個元素作為支點元素,Partition所產生的劃分80%以上都比9:1更對稱,而另20%則比9:1差,這里證明從略。
平均情況下,Partition產生的劃分中既有「好的」,又有「差的」。這時,與Partition執行過程對應的遞歸樹中,好、差劃分是隨機地分布在樹的各層上的。為與我們的直覺相一致,假設好、差劃分交替出現在樹的各層上,且好的劃分是最佳情況劃分,而差的劃分是最壞情況下的劃分。在根節點處,劃分的代價為n,劃分出來的兩個子表的大小為n-1和1,即最壞情況。在根的下一層,大小為n-1的子表按最佳情況劃分成大小各為(n-1)/2的兩個子表。這兒我們假設含1個元素的子表的邊界條件代價為1。
在一個差的劃分後接一個好的劃分後,產生出三個子表,大小各為1,(n-1)/2和(n-1)/2,代價共為2n-1=θ(n)。一層劃分就產生出大小為(n-1)/2+1和(n-1)/2的兩個子表,代價為n=θ(n)。這種劃分差不多是完全對稱的,比9:1的劃分要好。從直覺上看,差的劃分的代價θ(n)可被吸收到好的劃分的代價θ(n)中去,結果是一個好的劃分。這樣,當好、差劃分交替分布劃分都是好的一樣:仍是θ(nlogn),但θ記號中隱含的常數因子要略大一些。關於平均情況的嚴格分析將在後文給出。
在前文從直覺上探討快速排序的平均性態過程中,我們已假定輸入數據的所有排列都是等可能的。如果輸入的分布滿足這個假設時,快速排序是對足夠大的輸入的理想選擇。但在實際應用中,這個假設就不會總是成立。
解決的方法是,利用隨機化策略,能夠克服分布的等可能性假設所帶來的問題。
一種隨機化策略是:與對輸入的分布作「假設」不同的是對輸入的分布作「規定」。具體地說,在排序輸入的線性表前,對其元素加以隨機排列,以強制的方法使每種排列滿足等可能性。事實上,我們可以找到一個能在O(n)時間內對含n個元素的數組加以隨機排列的演算法。這種修改不改變演算法的最壞情況運行時間,但它卻使得運行時間能夠獨立於輸入數據已排序的情況。
另一種隨機化策略是:利用前文介紹的選擇支點元素pivot的第四種方法,即隨機地在L[p..r]中選擇一個元素作為支點元素pivot。實際應用中通常採用這種方法。
快速排序的隨機化版本有一個和其他隨機化演算法一樣的有趣性質:沒有一個特別的輸入會導致最壞情況性態。這種演算法的最壞情況性態是由隨機數產生器決定的。你即使有意給出一個壞的輸入也沒用,因為隨機化排列會使得輸入數據的次序對演算法不產生影響。只有在隨機數產生器給出了一個很不巧的排列時,隨機化演算法的最壞情況性態才會出現。事實上可以證明幾乎所有的排列都可使快速排序接近平均情況性態,只有非常少的幾個排列才會導致演算法的近最壞情況性態。
一般來說,當一個演算法可按多條路子做下去,但又很難決定哪一條保證是好的選擇時,隨機化策略是很有用的。如果大部分選擇都是好的,則隨機地選一個就行了。通常,一個演算法在其執行過程中要做很多選擇。如果一個好的選擇的獲益大於壞的選擇的代價,那麼隨機地做一個選擇就能得到一個很有效的演算法。我們在前文已經了解到,對快速排序來說,一組好壞相雜的劃分仍能產生很好的運行時間 。因此我們可以認為該演算法的隨機化版本也能具有較好的性態。
『肆』 幾種經典排序演算法優劣比較的C++程序實現
一、低級排序演算法
1.選擇排序
(1)排序過程
給定一個數值集合,循環遍歷集合,每次遍歷從集合中選擇出最小或最大的放入集合的開頭或結尾的位置,下次循環從剩餘的元素集合中遍歷找出最小的並如上操作,最後直至所有原集合元素都遍歷完畢,排序結束。
(2)實現代碼
//選擇排序法
template
void Sort::SelectSort(T* array, int size)
{
int minIndex;
for(int i = 0; i < size; i++)
{
minIndex = i;
for(int j = i + 1; j < size; j++)
{
if(array[minIndex] > array[j])
{
minIndex = j;
}
}
if(minIndex != i)
{
Swap(array, i, minIndex);
}
}
}
(3)分析總結
選擇排序時間復雜度比較高,達到了O(n^2),每次選擇都要遍歷一遍無序區間。選擇排序對一類重要的元素序列具有較好的效率,就是元素規模很大,而排序碼卻比較小的序列。另外要說明的是選擇排序是一種不穩定的排序方法。
2.冒泡排序
(1)排序過程
冒泡排序的過程形如其名,就是依次比較相鄰兩個元素,優先順序高(或大或小)的元素向後移動,直至到達序列末尾,無序區間就會相應地縮小。下一次再從無序區間進行冒泡操作,依此循環直至無序區間為1,排序結束。
(2)實現代碼
//冒泡排序法
template
void Sort::BubbleSort(T* array, int size)
{
for(int i = 0; i < size; i++)
{
for(int j = 1; j < size - i; j++)
{
if(array[j] < array[j - 1])
{
Swap(array, j, j - 1);
}
}
}
}
(3)分析總結
冒泡排序的時間復雜度也比較高,達到O(n^2),每次遍歷無序區間都將優先順序高的元素移動到無序區間的末尾。冒泡排序是一種穩定的排序方式。
二、高級排序演算法
(1)排序過程
歸並排序的原理比較簡單,也是基於分治思想的。它將待排序的元素序列分成兩個長度相等的子序列,然後為每一個子序列排序,然後再將它們合並成一個序列。
(2)實現代碼
//歸並排序
template
void Sort::MergeSort(T* array, int left, int right)
{
if(left < right)
{
int mid = (left + right) / 2;
MergeSort(array, left, mid);
MergeSort(array, mid + 1, right);
Merge(array, left, mid, right);
}
}
//合並兩個已排好序的子鏈
template
void Sort::Merge(T* array, int left, int mid, int right)
{
T* temp = new T[right - left + 1];
int i = left, j = mid + 1, m = 0;
while(i <= mid && j <= right)
{
if(array[i] < array[j])
{
temp[m++] = array[i++];
}
else
{
temp[m++] = array[j++];
}
}
while(i <= mid)
{
temp[m++] = array[i++];
}
while(j <= right)
{
temp[m++] = array[j++];
}
for(int n = left, m = 0; n <= right; n++, m++)
{
array[n] = temp[m];
}
delete temp;
}
(3)分析總結
歸並排序最好、最差和平均時間復雜度都是O(nlogn),是一種穩定的排序演算法。
『伍』 c語言的兩種排序
1、選擇排序法
要求輸入10個整數,從大到小排序輸出
輸入:2 0 3 -4 8 9 5 1 7 6
輸出:9 8 7 6 5 3 2 1 0 -4
代碼:
#include<stdio.h>
int main(int argc,const char*argv[]){
int num[10],i,j,k,l,temp;
//用一個數組保存輸入的數據
for(i=0;i<=9;i++)
{
scanf("%d",&num<i>);
}
//用兩個for嵌套循環來進行數據大小比較進行排序
for(j=0;j<9;j++)
{
for(k=j+1;k<=9;k++)
{
if(num[j]<num[k])//num[j]<num[k]
{
temp=num[j];
num[j]=num[k];
num[k]=temp;
}
}
}
//用一個for循環來輸出數組中排序好的數據
for(l=0;l<=9;l++)
{
printf("%d",num[l]);
}
return 0;
}
2、冒泡排序法
要求輸入10個整數,從大到小排序輸出
輸入:2 0 3-4 8 9 5 1 7 6
輸出:9 8 7 6 5 3 2 1 0-4
代碼:
#include<stdio.h>
int main(int argc,const char*argv[]){
//用一個數組來存數據
int num[10],i,j,k,l,temp;
//用for來把數據一個一個讀取進來
for(i=0;i<=9;i++)
{
scanf("%d",&num<i>);
}
//用兩次層for循環來比較數據,進行冒泡
for(j=0;j<9;j++)
{
for(k=0;k<9-j;k++)
{
if(num[k]<num[k+1])//num[k]<num[k+1]
{
temp=num[k];
num[k]=num[k+1];
num[k+1]=temp;
}
}
}
//用一個for循環來輸出數組中排序好的數據
for(l=0;l<=9;l++)
{
printf("%d",num[l]);
}
return 0;
}
return 0代表程序正常退出。return是C++預定義的語句,它提供了終止函數執行的一種方式。當return語句提供了一個值時,這個值就成為函數的返回值。
return語句用來結束循環,或返回一個函數的值。
1、return 0,說明程序正常退出,返回到主程序繼續往下執行。
2、return 1,說明程序異常退出,返回主調函數來處理,繼續往下執行。return 0或return 1對程序執行的順序沒有影響,只是大家習慣於使用return(0)退出子程序而已。
『陸』 c語言編程:對10個數冒泡排序(升序)。
#include<stdio.h>
intmain(){
intnumber[10]={95,45,15,78,84,51,24,12,34,23};
for(int j=0;j< 9;j++)
for(int i=0;i< 9 -j;i++) {
if(a[i]>a[i+1]) {
int temp=a[i];a[i]=a[i+1];
a[i+1]=temp; }
}
for(int i=0;i< 10;i++){
printf(「%d」,a[i]); }
}
插入排序
已知一組升序排列數據a[1]、a[2]、……a[n],一組無序數據b[1]、b[2]、……b[m],需將二者合並成一個升序數列。
首先比較b[1]與a[1]的值,若b[1]大於a[1],則跳過,比較b[1]與a[2]的值,若b[1]仍然大於a[2],則繼續跳過,直到b[1]小於a數組中某一數據a[x],則將a[x]~a[n]分別向後移動一位,將b[1]插入到原來a[x]的位置這就完成了b[1]的插入。
b[2]~b[m]用相同方法插入。
快速排序
快速排序是大家已知的常用排序演算法中最快的排序方法。已知一組無序數據a[1]、a[2]、……a[n],需將其按升序排列。首先任取數據a[x]作為基準。
比較a[x]與其它數據並排序,使a[x]排在數據的第k位,並且使a[1]~a[k-1]中的每一個數據<a[x],a[k+1]~a[n]中的每一個數據>a[x],然後採用分治的策略分別對a[1]~a[k-1]和a[k+1]~a[n]兩組數據進行快速排序。
希爾排序
已知一組無序數據a[1]、a[2]、……a[n],需將其按升序排列。
首先取一增量d(d<n),將a[1]、a[1+d]、a[1+2d]……列為第一組,a[2]、a[2+d]、a[2+2d]……列為第二組……,a[d]、a[2d]、a[3d]……列為最後一組以次類推,在各組內用插入排序,然後取d'<d,重復上述操作,直到d=1。