2021-11-09
關鍵字:桶排序
1、桶排序
桶排序一般用於對一組知道上下限的整數序列中。
因爲桶排序的核心原理就是全覆蓋式計數,爲整個區間每一個數創建一個計數器,遍歷待排序序列,爲每一個出現的數計數加1,最後根據需要從頭至尾或從尾至頭打印區間計數。
舉個例子,假設要爲一個班級的學生數學考試分數做個排序。
已知分數範圍爲 0 ~ 100,是一個知道上下限的區間。現有分數:52、70、41、99、64、64。創建一個101個長度的數組score,第0號表示成績爲0分的人數,第1號爲成績爲1分的人數,第101號爲成績爲100分的人數。遍歷待排序序列,第一個成績爲52,則將score數組第53號的值加1,第二個成績爲70,則將score數組第71號的值加1,依此類推直至結尾。當我們需要獲取排序結果時,例如想要將成績從高到低打印出來,則可以逆遍歷score數組,當其計數值大於0時則打印出來即可。
桶排序的優點就是速度快,缺點是對序列元素有要求,且其空間佔用會隨着序列規模的增大而增大。
以下是一個桶排序的C語言實例:
#include <stdio.h> #include <string.h> int main() { //待排序序列 const char scores[] = {71, 100, 7, 40, 88, 100, 91, 96, 71, 84, 60, 40, 71, 0, 1, 0}; //序列上下限爲 0 ~ 100 的整數。 char score_bucket[101]; memset(&score_bucket, 0, 101); int count = sizeof(scores) / sizeof(char); int i; for(i = 0; i < count; i++) { score_bucket[scores[i]] += 1; } //打印結果(遞減順序) int j = 0; for(i = 100; i >= 0; i--) { if(score_bucket[i]) { for(j = 0; j < score_bucket[i]; j++) { printf("%d,", i); } } } printf("\n"); return 0; }
2、冒泡排序
冒泡排序對空間的要求非常小,時間複雜度爲O(n(n+1))約等於O(n2)。
其核心原理是從頭到尾兩兩比較,將順序不正確的兩個數互換一下位置,如此一趟下來能將符合要求的最大或最小數挪到原數據的最末位,然後再重複一次操作,只不過這次因爲最末一位已經是正確的順序了,第二輪比較就只到第n-1位了。如此循環直至原數組中已排序序列數量漲至n-1個爲止即表示整個排序已完成。
以下是冒泡排序的C語言實例:
#include <stdio.h> int main() { //待排序序列 char scores[] = {71, 100, 7, 40, 88, 100, 91, 96, 71, 84, 60, 40, 71, 0, 1, 0}; int count = sizeof(scores) / sizeof(char); //序列長度 int i; int j; int loop = count - 1; //最後一個元素不用參與到排序中。 char tmp; for(i = 0; i < loop; i++) { for(j = 0; j < loop - i; j++) { if(scores[j] > scores[j + 1]) { tmp = scores[j]; scores[j] = scores[j+1]; scores[j+1] = tmp; } } } //print for(i = 0; i < count; i++) { printf("%d,", scores[i]); } printf("\n"); return 0; }
3、快速排序
桶排序速度快,但是空間消耗大。冒泡排序空間消耗小,但是速度慢。快速排序則可以一定程度上兼顧空間和時間消耗,它在數值比較上採用與冒泡排序類似的兩兩交換法,在空間消耗上採用函數遞歸調用,雖然遞歸調用函數也會隨着序列規模上升而增長,但相對來講空間佔用還是比桶排序好很多。
快速排序的核心是選中一個基準數,在遞增排序的需求下將所有大於基準數的元素放在基準數右邊,將所有小於基準數的元素放在其左邊。然後對分別對左半子序列和右半子序列應用同樣的算法,直至子序列只剩一個元素爲止。通常爲了方便,基準數選第1個或最後一個。
快速排序的實現原理是將待排序序列第1個元素作爲基準數,分別從序列兩端收縮遍歷取值。如果基準數選的是第1個,則首先從右端收縮,若基準數選的是最後一個,則首先從左端收縮。當從右端開始收縮時,一直收縮直到遇到一個數小於基準數爲止,然後收縮左端,直到找到一個數大於基準數爲止。然後直接交換這兩個數,之後再重複兩端的收縮過程,直至兩端收縮碰撞爲止。收縮碰撞後將碰撞值直接與基準數對調,之後便可對生成的兩個子序列再分別應用此算法,直至子序列只有一個元素爲止。
以下是一個C語言版的實例:
#include <stdio.h>static void quick_sort(char nums[], int start, int end) { char tmp; int i = start; int j = end; if(start >= end) return; while(1) { if(i == j) { //直接跟基數交換 tmp = nums[start]; nums[start] = nums[j]; nums[j] = tmp; //迭代左邊的 if(start < j) { quick_sort(nums, start, j - 1); } //迭代右邊的 if(end > i) { quick_sort(nums, i + 1, end); } //排序完成 break; } else { //j先動。 if(nums[j] < nums[start]) { //輪到i動 if(nums[i] > nums[start]) { if(i != j) { //可以交換 tmp = nums[j]; nums[j] = nums[i]; nums[i] = tmp; } } else { i++; } } else { j--; } } } } int main() { //待排序序列 char nums[] = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; int i; quick_sort(nums, 0, 9); for(i = 0; i < 10; i++) { printf("%d, ", nums[i]); } printf("\n"); return 0; }