轉載註明出處:轉載
在開發中,對一組數據進行有序地排列是經常需要做的事情,所以掌握幾種甚至更多的排序算法是絕對有必要的
本文章介紹的是排序算法中較簡單的一種算法:冒泡排序
注意:在深入學習更多排序算法後和在實際使用情況中,冒泡排序的使用還是極少的。它適合數據規模很小的時候,而且它的效率也比較低,但是作爲入門的排序算法,還是值得學習的
先嚐試用最簡單的想法去實現排序,以此來比較學習冒泡排序
問題:設有一數組,其大小爲10個元素(int str[10])數組內的數據是無序。現在要求我們通過編程將這個無序的數組變成一個從小到大排序的數組(從下標爲0開始)
思路:按照題目的要求,毫無疑問,正確的結果應該就像這樣: 1 2 3 4 5 6 7 8 9 10 要做到這樣,最簡單和最直接想到的方法就是進行對比交換。
首先,把10個數裏最小的個數放到下標爲0的位置上(str[0])
通過將下標爲0的數(str[0])與剩下其餘9個數進行對比交換(將較少者放置在下標爲0的位置上),就可以得到這10個數最小的那個
10個數最小的那位確定後,接下來就要找剩下9個數最小的那個。
因爲已經確定出一個最小的數,所以就不要動str[0],直接從str[1]開始,與剩下的8個數對比交換,找出9個數中最小的那位放到下標爲1(str[1])的位置上
繼續按照這個思路就可以將這十個數變成有序的(從小到大)的數組
void swap(int *a, int *b); //交換兩個數
int main()
{
int str[10];
int i, j;
//初始化數組爲10 9 8 7 6 5 4 3 2 1
for (i = 0; i < 10; i++)
{
str[i] = 10 - i;
}
//排序,從a[0]開始排,從小到大
for (i = 0; i < 10; i++)
{
for (j = i + 1; j < 10; j++)
{
if (str[i] > str[j])
{
swap(&str[i], &str[j]);
}
}
}
//將十個數輸出
for (i = 0; i < 10; i++)
printf("%d\n", str[i]);
return 0;
}
void swap(int *a, int *b)
{
int c;
c = *a;
*a = *b;
*b = c;
}
這個方法是比較容易想到的實現方法。但存在不足:就是本來位於前面的較小數被交換到後面
演示:
開始:9 4 5 6 8 3 2 7 10 1 (下標從左到右分別是0~9)按照上面的程序進行對比交換
第一次:4 9 5 6 8 3 2 7 10 1
第二次:4 9 5 6 8 3 2 7 10 1
。。。:(沒有交換)
第五次:3 9 5 6 8 4 2 7 10 1
第六次:2 9 5 6 8 3 4 7 10 1
。。。:(沒有交換)
第十次:1 9 5 6 8 3 4 7 10 2
可以看出,原來較小的數是在前面的,經過一輪的交換後放到後面了
那麼怎樣解決這個不足呢?可以使用冒泡排序
什麼是冒泡排序呢?
你可以這樣理解:(從小到大排序)存在10個不同大小的氣泡,由底至上地把較少的氣泡逐步地向上升,這樣經過遍歷一次後,最小的氣泡就會被上升到頂(下標爲0),然後再從底至上地這樣升,循環直至十個氣泡大小有序。
在冒泡排序中,最重要的思想是兩兩比較,將兩者較少的升上去
冒泡排序最壞情況的時間複雜度是O(n²)
#include <stdio.h>
void swap(int *a, int *b);
int main()
{
int array[10] = {15, 225, 34, 42, 52, 6, 7856, 865, 954, 10};
int i, j;
for (i = 0; i < 10; i++)
{
//每一次由底至上地上升
for (j = 9; j > i; j--)
{
if (array[j] < array[j-1])
{
swap(&array[j], &array[j-1]);
}
}
}
for (i = 0; i < 10; i++)
{
printf("%d\n", array[i]);
}
return 0;
}
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
冒泡排序算法只會將較少的逐步向上推,不會造成文章前面所說的不足,這裏就不給予演示。
有些追求完美的人就會思考,冒泡排序能不能優化呢?
答案是能的。
根據上面的內容,可以知道冒泡排序的核心是兩兩對比進行交換。如果有一個無序數列(2,1,3,4,5,6,7,8,9,10)
按照上面文章的代碼,從第一次循環交換後的操作,可以說都是沒必要的。所以,這些操作就是我們需要優化的地方。
那麼如何優化?
通過觀察可以看到,造成沒必要的操作主要原因是後面8個數的順序都已經是有序。所以,我們可以通過設置一個標記變量,標記數列中的數是否在循環結束前就已經排好序
#include <stdio.h>
void swap(int *a, int *b);
int main()
{
int array[10] = {2, 1, 3, 4, 5, 6, 7, 8, 9, 10};
int i, j;
int flag = 1; //設置標記變量
for (i = 0; i < 10 && flag; i++)
{
flag = 0; //只要flag在下一次外循環條件檢測的時候值爲0,就說明已經排好序,不用繼續循環
for (j = 9; j > i; j--)
{
if (array[j] < array[j-1])
{
swap(&array[j], &array[j-1]);
flag = 1; //如果有交換,就將標記變量賦1
}
}
}
for (i = 0; i < 10; i++)
{
printf("%d\n", array[i]);
}
return 0;
}
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
根據優化過的代碼,當最好情況的時候,冒泡排序的時間複雜度是O(n)。