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; }