計數排序其實是桶排序的一種特殊情況。當要排序的 n 個資料,所處的範圍並不大的時候,比如最大值是 k,我們就可以把資料劃分成 k 個桶。每個桶內的資料值都是相同的,省掉了桶內排序的時間。
我們都經歷過高考,高考查分數系統你還記得嗎?我們查分數的時候,系統會顯示我們的成績以及所在省的排名。如果你所在的省有 50 萬考生,如何通過成績快速排序得出名次呢?
考生的滿分是 900 分,最小是 0 分,這個資料的範圍很小,所以我們可以分成 901 個桶,對應分數從 0 分到 900 分。根據考生的成績,我們將這 50 萬考生劃分到這 901 個桶裡。桶內的資料都是分數相同的考生,所以並不需要再進行排序。我們只需要依次掃瞄每個桶,將桶內的考生依次輸出到乙個陣列中,就實現了 50 萬考生的排序。因為只涉及掃瞄遍歷操作,所以時間複雜度是 o(n)。
我們假設有 8 個考生,分數在 0 到 5 分之間。這 8 個考生的成績我們放在乙個陣列 a[8]中,它們分別是:2,5,3,0,2,3,0,3。
考生的成績從 0 到 5 分,我們使用大小為 6 的陣列 c[6]表示桶,其中下標對應分數。不過,c[6]內儲存的並不是考生,而是對應的考生個數。像我剛剛舉的那個例子,我們只需要遍歷一遍考生分數,就可以得到 c[6]的值。
這是我們的陣列,從圖中可以看出,分數為 3 分的考生有 3 個,小於 3 分的考生有 4 個,所以,成績為 3 分的考生在排序之後的有序陣列 r[8]中,會儲存下標 4,5,6 的位置。
那我們如何快速計算出,每個分數的考生在有序陣列中對應的儲存位置呢?
我們對 c[6]陣列順序求和,c[6]儲存的資料就變成了下面這樣子。c[k]裡儲存小於等於分數 k 的考生個數。
我們從後到前依次掃瞄陣列 a。比如,當掃瞄到 3 時,我們可以從陣列 c 中取出下標為 3 的值 7,也就是說,到目前為止,包括自己在內,分數小於等於 3 的考生有 7 個,也就是說 3 是陣列 r 中的第 7 個元素(也就是陣列 r 中下標為 6 的位置)。當 3 放入到陣列 r 中後,小於等於 3 的元素就只剩下了 6 個了,所以相應的 c[3]要減 1,變成 6。
以此類推,當我們掃瞄到第 2 個分數為 3 的考生的時候,就會把它放入陣列 r 中的第 6 個元素的位置(也就是下標為 5 的位置)。當我們掃瞄完整個陣列 a 後,陣列 r 內的資料就是按照分數從小到大有序排列的了。
下面我簡單劃分了幾個步驟:
找出原陣列中元素值最大的,記為max
。
建立乙個新陣列count
,其長度是max
加1,其元素預設值都為0。
遍歷原陣列中的元素,以原陣列中的元素作為count
陣列的索引,以原陣列中的元素出現次數作為count
陣列的元素值。
建立臨時陣列tmparr
。
遍歷count
陣列,找出其中元素值大於0的元素,將其對應的索引作為元素值填充到result
陣列中去,每處理一次,count
中的該元素值減1,直到該元素值不大於0,依次處理count
中剩下的元素。
將臨時陣列賦值給原陣列。
下面是**實現:
// 計數排序,a是陣列,n是陣列大小。假設陣列中儲存的都是非負整數。
public void countingsort(int arr, int n)
}int countarr = new int[max + 1];
// 計算每個元素的個數,放入countarr中
for (int i = 0; i < arr.length; i++)
// 依次累加
for (int i = 1; i < countarr.length; i++)
//遍歷陣列
//倒序遍歷,保證了演算法的穩定性
int tmparr = new int[n];
for (int i = arr.length - 1; i >= 0; i--)
system.arraycopy(tmparr, 0, arr, 0, n);
}
使用條件1)只能用在資料範圍不大的場景中,若資料範圍k比要排序的資料n大很多,就不適合用計數排序;
2)計數排序只能給非負整數排序,其他型別需要在不改變相對大小情況下,轉換為非負整數;
思考
上述方法可以解決一般的情況,但是會存在空間浪費的情況。
比如一組資料,其中最大值為112,按照基礎版的思路,我們需要建立乙個長度為113的計數陣列,但是我們可以發現,它前面的
[0,99]
的空間完全浪費了,那怎樣優化呢?
將陣列長度定為max-min+1
,即不僅要找出最大值,還要找出最小值,根據兩者的差來確定計數陣列的長度。
public void countingsort(int arr, int n)
if (arr[i] < min)
}int countarr = new int[max - min + 1];
// 計算每個元素的個數,放入countarr中
for (int i = 0; i < arr.length; i++)
// 依次累加
for (int i = 1; i < countarr.length; i++)
//遍歷陣列(倒序-穩定性)
int tmparr = new int[n];
for (int i = arr.length - 1; i >= 0; i--)
system.arraycopy(tmparr, 0, arr, 0, tmparr.length);
}
什麼是計數排序?
public static int countsort int array 2.根據數列最大值確定統計陣列的長度 int countarray newint max 1 3.遍歷數列,填充統計陣列 for inti 0 i countarray array i 4.遍歷統計陣列,輸出結果 intin...
什麼是計數排序?
有這樣一道排序題 陣列裡有20個隨機數,取值範圍為從0到10,要求用最快的速度把這20個整數從小到大進行排序。第一時間你可能會想使用快速排序,因為快排的時間複雜度只有o nlogn 但是這種方法還是不夠快,有沒有比o nlogn 更快的排序方法呢?你可能會有疑問 o nlogn 已經是最快的排序演算...
什麼是計數排序?
假設,有20個隨機整數,取值範圍是0到10,需要對其排序。可能第一反應是使用快速排序啊,快排的時間複雜度是o nlog n 但是,可不可以比o nlog n 更快呢?這就是這篇文章要介紹的計數排序 從名字上來看,就是計算數字出現頻次的排序方法,非常的見名知意 因為取值範圍是0到10 整數的值在0,1...