計數排序(counting sort)
計數排序並不基於元素的比較,而是一種利用數組下標來確定元素正確位置的算法。計數排序的核心在於將輸入的數據值轉化為鍵值存儲在額外開辟的數組空間中。作為一種線性時間復雜度的排序,計數排序算法的時間復雜度O(n + k)(k為整數的范圍)。
簡單描述就是,在一個有確定范圍的整數空間中,建立一個長度更大的數組,如當輸入的元素是 n 個 0 到 k 之間的整數時,建立一個長度大於等於k的數組。該數組的每一個下標位置的值代表了數組中對應整數出現的次數。根據這個統計結果,直接遍歷數組,輸出數組元素的下標值,元素的值是幾, 就輸出幾次。
簡單實現:
class Solution { public: void coutSort(int* data, int length) { if (data == nullptr || length <= 0) return; //確定數列最大值 int max = data[0]; for (int i = 1; i < length; ++i) { if (data[i] > max) max = data[i]; } // 確定統計數組長度並進行初始化 int* coutData = new int[max + 1]; for (int i = 0; i <= max; ++i) coutData[i] = 0; // 遍歷數組,統計每個數出現的次數 for (int i = 0; i < length; ++i) ++coutData[data[i]]; // 排序數組,某個數出現了幾次,便在data里累計輸出幾次 int index = 0; for (int i = 0; i <= max; ++i) { for (int j = 0; j < coutData[i]; ++j) { data[index++] = i; } } } };
優化版(穩定排序):
(1)找出待排序的數組中最大和最小的元素
(2)統計數組中每個值為i的元素出現的次數,存入數組的第i項
(3)對所有的計數累加(從數組中的第一個元素開始,每一項和前一項相加)
(4)反向填充目標數組:將每個元素i放在新數組的第(i)項,每放一個元素就將(i)減去1
class Solution { public: int* coutSort(int* data, int length) { if (data == nullptr || length <= 0) return nullptr; //確定數列最大值 int max = data[0]; int min = data[0]; for (int i = 1; i < length; ++i) { if (data[i] > max) max = data[i]; if (data[i] < min) min = data[i]; } int d = max - min; // 確定統計數組長度並進行初始化 int* coutData = new int[d + 1]; for (int i = 0; i <= d; ++i) coutData[i] = 0; // 遍歷數組,統計每個數出現的次數 for (int i = 0; i < length; ++i) ++coutData[data[i] - min]; // 統計數組做變形,后面的元素等於前面的元素之和 for (int i = 1; i <= d; ++i) coutData[i] += coutData[i - 1]; // 倒序遍歷原始數列,從統計數組找到正確的位置,輸出到結果數組 int* sortedArray = new int[length]; for (int i = length - 1; i >= 0; i--) { sortedArray[coutData[data[i] - min] - 1] = data[i]; // 找到data[i]對應的coutData的值,值為多少,表示原來排序多少,(因為從1開始,所以再減1) coutData[data[i] - min]--; // 然后將相應的coutData的值減1,表示下次再遇到此值時,原來的排序是多少。 } return sortedArray; } };
優化版本的測試:
void test() { int data[] = { 95, 98, 97, 90, 98, 93, 92, 91 }; int length = sizeof(data) / sizeof(int); Solution M; int* array = M.coutSort(data, length); for (int i = 0; i < length; ++i) cout << array[i] << " "; cout << endl; }
局限:
1. 當數組最大和最小值差距過大時,並不適合用計數排序。會造成空間浪費,時間復雜度也會隨之升高。
2. 當數組元素不是整數時,如小數, 也不適合用計數排序。