計數排序的簡單實現
阿新 • • 發佈:2019-01-05
計數排序的原理比較簡單,但是很巧妙。有點類似於桶排序,但是有一些區別。
具體思想是,給定一個數組,先統計各個元素出現的次數,用元素的值做下標,得到一個新的陣列。然後掃描這個陣列,對於陣列的每個下標,如果它對應的值不為零,說明原來陣列中就有幾個這樣的值。由於下標的天然遞增,依次將這些值展開就得到了排序後的陣列。
但是計數排序有它的侷限性,首先如果想用陣列統計各個元素出現的次數,它的值必須是正整數。如果你想用map來統計次數的話,那麼就無法保證下標的遞增特性。(某些語言的map實現可能會自動按照下標排序,但複雜度就是另一回事了)。
最後,計數排序的複雜度是O(n+k),n為元素個數,k為最大值。程式碼如下:
// countSort.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <iostream> #include <vector> using namespace std; vector<int> countSort(vector<int>& v) { //找到最大值 int max = -1000000; for (auto i : v) if (i >= max) max = i; //掃描一遍,記錄各元素出現的次數 vector<int> c; c.resize(max + 1); for (auto i : v) c[i]++; //對出現的每個元素,按照其出現的次數,依次展開到目標陣列 vector<int> ans; for (int i = 0; i < c.size(); i++) { if (c[i] != 0) { int temp = c[i]; while (temp--) ans.push_back(i); } } return ans; } int main() { vector<int> a = { 8,2,4,9,3,6,100 }; auto ans=countSort(a); for (auto i : ans) cout << i << endl; system("pause"); return 0; }
執行結果:
上面是按照我自己對計數排序的理解所寫的程式碼。
CLRS中採用了另外一種方法。在的得到每個數出現次數的陣列後,將這個陣列的每個數加上它前面的一個數。這樣得到的新陣列中,每個元素表示小於等於某個數(也就是下標)的個數。舉個栗子,比如說小於等於5的個數為6,則不管有幾個總共有幾個5,總會有最後一個5排在第5位(下標從0開始),這樣算上剩下的小於等於5的那些數,總共達到了6個。這個地方稍微有點繞,不過這就是CLRS上關於計數排序的關鍵所在。因此我們可以用這個性質對原始陣列中的元素再做一遍掃描,找到每個數的正確位置。程式碼如下:
// countSort.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <iostream> #include <vector> using namespace std; vector<int> countSort(vector<int>& v) { //找到最大值 int max = -1000000; for (auto i : v) if (i >= max) max = i; //掃描一遍,記錄各元素出現的次數 vector<int> c; c.resize(max + 1); for (auto i : v) c[i]++; //對出現的每個元素,按照其出現的次數,依次展開到目標陣列 vector<int> ans; for (int i = 0; i < c.size(); i++) { if (c[i] != 0) { int temp = c[i]; while (temp--) ans.push_back(i); } } return ans; } vector<int> countSort1(vector<int>& v) { //找到最大值 int max = -1000000; for (auto i : v) if (i >= max) max = i; //掃描一遍,記錄各元素出現的次數 vector<int> c; c.resize(max + 1); for (auto i : v) c[i]++; //小於等於每個數的個數 for (int i = 1; i < c.size(); i++) c[i] += c[i - 1]; vector<int> ans; ans.resize(v.size()); //for (int i = v.size() - 1; i >= 0; i--) for(int i=0;i<v.size();i++) { ans[c[v[i]] - 1] = v[i]; //例如小於等於某個數的個數為m,則這個數應該被放在第m-1位 --c[v[i]]; //同時,因為已經放置好了一個該數,所以計數值應該減一 } return ans; } int main() { vector<int> a = { 8,2,4,9,3,6,100 }; auto ans=countSort1(a); for (auto i : ans) cout << i << endl; system("pause"); return 0; }