冒泡排序
冒泡排序 Bubble_Sort,是極爲簡單的一種排序算法。雖然效率差一點,但好在具有結構簡單,容易理解,易於操作等優點。冒泡排序就是把小的元素往前調或者把大的元素往後調。在相鄰的兩個元素間比較,交換也發生在這兩個元素之間。
冒泡排序是一種穩定排序算法,在排序後相同元素的前後順序並沒有改變。
相比於傳統的冒泡排序,平均時間複雜度爲O(n2),最好的時間複雜度爲2,是一種效率不高的的排序。但勝在使用方便,於是便有了一些對於冒泡的優化算法。
這裏,我總結了以下兩種優化方案:
- 使用標記,在冒泡排序有序後提前退出排序
- 在兩個方向上來回進行進行冒泡,使待排數列每次縮短兩步
爲了不使程序太過複雜,這裏我們採用類似於波動曲線的一種方式進行方案二算法的設計。另外方案二與方案一也可以相互結合使用。三種排序算法特點如下所示:
測試三種排序效率
這裏我們直接開始測試三種排序的效率,通過以下代碼測試最終的優化排序方案,最後與標椎庫<stdlib.h>中提供的qsort()
函數進行比較。
輔助函數與主程序代碼部分
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 交換兩整型變量
void Swap_Int(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
// 輸出arr數組中start下標~stop下標
void Show_Ar(int* arr, int indexStart, int indexStop)
{
for (int i = indexStart; i < indexStop; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
/* 三種冒泡排序 */
void Bubble_Sort1(int *arr, int n);
void Bubble_Sort2(int* arr, int n);
void Bubble_Sort3(int* arr, int n);
// 測試各排序算法的速度
void Speed(void (*pFun)(int *, int ));
int main()
{
Speed(Bubble_Sort1);
Speed(Bubble_Sort2);
Speed(Bubble_Sort3);
return 0;
}
測試三種排序的速度方法
// 測試排序算法的速度
void Speed(void (*pFun)(int*, int))
{
// 測試對十萬個數字的排序
const int n = 100000;
// 用於測試排序功能是否正常
//int arr[n] = { 67,45,90,78,89,23,34,100,12,56 };
int arr[n];
// 隨機賦值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定義計時器
clock_t start, stop;
// 由於數據過多,這裏輸出排序後的 10000——10020
start = clock();
(*pFun)(arr, n);
Show_Ar(arr, 10000, 10020);
stop = clock();
// 輸出三種排序所用時間
printf("排序用時 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
三種冒泡排序方法
// 常規的冒泡函數
void Bubble_Sort1(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
}
}
}
}
// 優化——使用標記
void Bubble_Sort2(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{
// 優化:判斷有序
int flg = 0;
for (int j = 0; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
flg = 1;
}
}
if (flg == 0) break;
}
}
// 優化——正反雙向排序
void Bubble_Sort3(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{ // 優化:判斷有序
int flg = 0;
// 正着跑
int j;
for (j = i; j < n - i - 1; ++j)
{
if (arr[j] > arr[j + 1])
{
Swap_Int(&arr[j], &arr[j + 1]);
flg = 1;
}
}
if (flg == 0) break;
// 反着跑
for (int k = j - 1; k > i; --k)
{
if (arr[k] < arr[k - 1])
{
Swap_Int(&arr[k], &arr[k - 1]);
flg = 1;
}
}
if (flg == 0) break;
}
}
測試三個冒泡排序對10萬個數據進行排序後的運行結果:
第一次測試截圖:
第二次測試截圖:
第三次測試截圖:
qsort 排序算法測試
qsort() 是C標椎庫中自帶的一種快速排序算法,排序速度相當快,並且是一種泛型的排序算法。下面是 qsort() 的用法。
// <stdlib.h>
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
base -- 指向要排序的數組的第一個元素的指針。
nitems -- 由 base 指向的數組中元素的個數。
size -- 數組中每個元素的大小,以字節爲單位。
compar -- 用來比較兩個元素的函數。
上圖爲三種排序的執行結果,接下來使用 qsort()
函數進行排序測試。這裏也使用Speed() 函數對測試排序的算法進行封裝。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int Cmp_Int(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
// 輸出arr數組中start下標~stop下標
void Show_Ar(int* arr, int indexStart, int indexStop)
{
for (int i = indexStart; i < indexStop; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void Speed();
int main()
{
int n = 3;
while (n--)
{
Speed();
}
return 0;
}
// 測試排序算法的速度
void Speed()
{
// 測試對十萬個數字的排序
const int n = 100000;
int arr[n];
// 隨機賦值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定義計時器
clock_t start, stop;
// 由於數據過多,這裏輸出排序後的 10000——10020
start = clock();
qsort(arr, n, sizeof(int), Cmp_Int);
Show_Ar(arr, 10000, 10020);
stop = clock();
// 輸出排序所用時間
printf("排序用時 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
第一次排序結果:(由於排序速度過快,不到1秒中就完成了十萬個數據的排序)
由於我們計算機棧區的大小限制,不便於測試更大的數據。所以我們改從堆區申請一百萬數據進行排序。要知道棧由系統自動分配,速度較快,堆區由程序猿手動申請,且操作效率要慢於棧區。
修改源碼,其中///***////中內容爲修改處
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
int Cmp_Int(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
////////////////////////////////////////////////////////////////////////
// 輸出arr數組中start下標~stop下標,以step步長 //增加步長參數
void Show_Ar_Step(int* arr, int indexStart, int indexStop, int step)
{
for (int i = indexStart; i < indexStop; i+=step)
{
printf("%d ", arr[i]);
}
printf("\n");
}
///////////////////////////////////////////////////////////////////////
void Speed();
int main()
{
int n = 3;
while (n--)
{
Speed();
}
return 0;
}
// 測試排序算法的速度
void Speed()
{
// 測試對一千萬個數字的排序
const int n = 10000000; //////////////
//////////////////////////////////////////////////////////////////
int* arr = (int*)malloc(sizeof(int) * n); //堆區申請
assert(arr != NULL);
/////////////////////////////////////////////////////////////////
// 打印arr數組的容量
printf("共有數據 %d 個\n", n ); ////////////
// 隨機賦值
srand((unsigned)time(NULL));
for (int i = 0; i < n; ++i)
{
arr[i] = rand() % 1000;
}
// 定義計時器
clock_t start, stop;
// 由於數據過多,這裏輸出排序後的 100000——500000,步長爲10000
start = clock();
qsort(arr, n, sizeof(int), Cmp_Int);
Show_Ar_Step(arr, 100000, 300000,10000); //////////
stop = clock();
// 輸出排序所用時間
printf("排序用時 %d 秒\n", (stop - start) / CLOCKS_PER_SEC);
}
對一千萬數據進行排序後的輸出結果:
可以看到,qsort()
排序真的是非常的快,無論對冒泡排序怎樣的優化之間的差距還是非常的大。
以下,是本次測試的數據對比:
冒泡排序: 十萬數據 平均用時73秒 (😭完敗)
優化冒泡: 十萬數據 平均用時64秒 (😛有效果)
qsort排序: 千萬數據 平均用時 3秒 (😋完勝)
雖然,C標椎庫中爲我們實現了排序算法,我們也不能太過依賴標椎庫。每一種算法都有他獨特的思想在其中,在平時的練習中也要多加練習其他的排序算法。另外,qsort是基於快排優化而來,下次我們可以通過優化快排和qsort進行測試比較。
附:C語言泛型編程——泛型冒泡排序: https://blog.csdn.net/weixin_43919932/article/details/90573445