冒泡排序
冒泡排序(
BubbleSort
)是一種流行的排序算法,這個排序過程就像一個個向上(向右)冒泡的氣泡,最輕的氣泡先冒上來(到達R[n]位置),較重的氣泡後冒上來,因此形象的稱之爲冒泡排序.
對R[1]~R[n]這n個記錄的冒泡排序過程:
第一趟從第一個記錄R[1]開始到第n個記錄R[n]爲止,對(n- 1個對)相鄰的兩個記錄進行比較,若與排序要求相逆,則交換兩者的位置.
這樣,經過一趟的比較,交換後,具有最大關鍵值的記錄就會被交換到R[n]位置.
第二趟從第一個記錄R[1]開始到n - 1個記錄R[n-1]爲止繼續重複上述的比較與交換,這樣具有次大關鍵字的記錄就被交換到R[n-1]位置.
如此重複,在經過n - 1 趟這樣的比較與交換之後,R[1]~R[n]這n個記錄已按關鍵字有序.化簡得:
設數組長度爲N。
1. 比較相鄰的前後二個數據,如果前面數據大於後面的數據,就將二個數據交換。
2. 這樣對數組的第0個數據到N-1個數據進行一次遍歷後,最大的一個數據就“沉”到數組第N-1個位置。
3. N=N-1,如果N不爲0就重複前面二步,否則排序完成。
過程演示:
首先:
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
3 0 1 8 7 2 5 4 6 9
第一步:
//a[0] 比較 a[1] , 3 <---> 0,大,交換,得:
0 3 1 8 7 2 5 4 6 9
//a[1] 比較 a[2] , 3<--->1,大,交換,得:
0 1 3 8 7 2 5 4 6 9
//a[2] 比較 a[3] , 3<---> 8,小,不變,得:
0 1 3 8 7 2 5 4 6 9
//a[3] 比較 a[4] , 8<---> 7,大,交換,得:
0 1 3 7 8 2 5 4 6 9
//a[4] 比較 a[5] , 8<---> 2,大,交換,得:
0 1 3 7 2 8 5 4 6 9
//a[5] 比較 a[6] , 8<---> 5,大,交換,得:
0 1 3 7 2 5 8 4 6 9
//a[6] 比較 a[7] , 8<---> 4,大,交換,得:
0 1 3 7 2 5 4 8 6 9
//a[7] 比較 a[8] , 8<---> 6,大,交換,得:
0 1 3 7 2 5 4 6 8 9
//a[8] 比較 a[9] , 8<---> 9,小,不變,得:
0 1 3 7 2 5 4 6 8 9
這時,經過一次遍歷,我們得到了最大值已經被冒(放置)在最右邊,接下來,重複這樣的操作,到n-1的位置,得到次大.
第二次:
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
0 1 3 7 2 5 4 6 8 9
//a[0] 比較 a[1] , 0 <---> 3,小,不變,得:
0 1 3 7 2 5 4 6 8 9
//a[1] 比較 a[2] , 1 <---> 3,小,不變,得:
0 1 3 7 2 5 4 6 8 9
//a[2] 比較 a[3] , 3 <---> 7,小,不變,得:
0 1 3 7 2 5 4 6 8 9
//a[3] 比較 a[4] , 7 <---> 2,大,交換,得:
0 1 3 2 7 5 4 6 8 9
//a[4] 比較 a[5] , 7 <---> 5,大,交換,得:
0 1 3 2 5 7 4 6 8 9
//a[5] 比較 a[6] , 7 <---> 4,大,交換,得:
0 1 3 2 5 4 7 6 8 9
//a[6] 比較 a[7] , 7 <---> 6,大,交換,得:
0 1 3 2 5 4 6 7 8 9
//a[7] 比較 a[8] , 7 <---> 8,小,不變,得:
0 1 3 2 5 4 6 7 8 9
...
我們可以看到,通過第二次,我們得到了次大元素並放入右邊倒數第二個位置上,我們若繼續重複,通過得到剩餘元素中最大值,可以得到整個元素組裏大小排列出來的次序.即可完成排序過程.
程序代碼:
按照此定義,我們很容易寫出冒泡排序的程序代碼:
void BubbleSort1(int *a, int size)
{
if(NULL == a || size < 0)
{
return;
}
int i = 0 ;
int j = 0 ;
int n = size;
for(i = 0; i < n; i++)
{
for(j = 1; j < n - i; j++)
{
if(a[j-1] > a[j])
{
Swap(&a[j-1], &a[j]);
}
}
}
}
即是將元素依次比較,若反序則交換.
我們可以看出,這樣的流程會有很多重複.那麼我們該怎麼樣進行優化呢?
如果是一個已經排好序的序列使用上述的方案進行排序的話,則在第一次的循環中則不會出現交換的過程(因爲前一個數據的值總是小於後一個數據),所以我們以是否發生了交換作爲排序是否結束的標誌。及如果一輪排序中並沒有發生交換過程則說明當前的序列是已序的。
0 1 2 3 5 8 9 4 6 7
比如這個序列,基本有序,當我們排好後面的元素後,前面已經排好序,那麼我們就可以少進行比較.
我們可以設置一個標誌,如果這一趟發生了交換,則爲true,否則爲false。如果明顯如果有一趟沒有發生交換,說明排序已經完成。
#define TRUE 1
#define FALSE 0
typedef unsigned char Boolean;
void BubbleSort2(int *a, int size)
{
if(NULL == a || size < 0)
{
return;
}
int j = 0 ;
int n = size;
Boolean flag = TRUE;
while(flag)
{
flag = FALSE;
for(j = 1; j < n; j++)
{
if(a[j-1] > a[j])
{
Swap(&a[j-1], &a[j]);
flag = TRUE;
}
}
n--;
}
}
進一步優化,可以一個標記flag來表示當前的狀態,在每輪比較的開始把flag置爲0,則代表沒有發生排序,如果發生了排序,則把flag值爲1,在下次的循環中繼續進行比較。
如果有100個數的數組,僅前面10個無序,後面90個都已排好序且都大於前面10個數字,那麼在第一趟遍歷後,最後發生交換的位置必定小於10,且這個位置之後的數據必定已經有序了,記錄下這位置,第二次只要從數組頭部遍歷到這個位置就可以了。
void BubbleSort3(int *a, int size)
{
if(NULL == a || size < 0)
{
return;
}
int j = 0 ;
int n = size;
int flag = n;
while(flag > 0)
{
n = flag;
flag = 0;
for(j = 1; j < n; j++)
{
if(a[j-1] > a[j])
{
Swap(&a[j-1], &a[j]);
flag = j;
}
}
}
}
功能檢測
- 檢測代碼:
int main()
{
int i =0;
int a[] = {3,0,1,8,7,2,5,4,6,9};
int n = sizeof(a)/sizeof(a[0]);
BubbleSort1(a, n);
// BubbleSort2(a, n);
// BubbleSort3(a, n);
printf("\n");
for(i = 0; i < n ; ++i)
{
printf("%3d",a[i]);
}
printf("\n");
return 0;
}
- 運行結果:
root@aemonair:~/Desktop# cc.sh BubbleSort.c
Compiling ...
-e CC BubbleSort.c -g -lpthread
-e Completed .
-e Sat Jul 23 18:42:01 CST 2016
root@aemonair:~/Desktop# ./BubbleSort
0 1 2 3 4 5 6 7 8 9
冒泡排序畢竟是一種效率低下的排序方法,在數據規模很小時,可以採用。數據規模比較大時,最好用其它排序方法。