iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0

Counting sort

Input : https://chart.googleapis.com/chart?cht=tx&chl=A%5Cbegin%7Bbmatrix%7Da_1%2Ca_2%2C...%2Ca_n%5Cend%7Bbmatrix%7D%2C%20A%5Ba_i%5D%20%5Cin%20%5Cbegin%7BBmatrix%7D1%2C2%2C3%2C...%2Ck%5Cend%7BBmatrix%7D
Output : https://chart.googleapis.com/chart?cht=tx&chl=B%5Cbegin%7Bbmatrix%7Db_1%2Cb_2%2C...%2Cb_n%5Cend%7Bbmatrix%7D%2Cb_1%20%3C%3D%20b_2%20%3C%3D%20...b_n
Aux(auxiliary) array : https://chart.googleapis.com/chart?cht=tx&chl=C%5Cbegin%7Bbmatrix%7D1%2C2...%2Ck%5Cend%7Bbmatrix%7D

Counting sort假設一個陣列中有https://chart.googleapis.com/chart?cht=tx&chl=n個整數,每一個整數都是在0到k範圍內。

對於每一個輸入的元素x,確認小於x的元素個數,就可以把x放在陣列中正確的位置上了

COUNTING-SORT(A, B, k)
    let C[0...k] be a new array
    for i = 0 to k
        C[i] = 0
    for j = 1 to A.length
        C[A[j]] = C[A[j]] + 1
    //C[i] now contains the number of elements equal to i.
    for i = 1 to k
        C[i] = C[i] + C[i - 1]
    //C[i] now contains the number of elements less than or equal to i.
    for j = A.length downto 1
        B[C[A[j]]] = A[j]
        C[A[j]] = C[A[j]] - 1

Example:

A = 4,1,3,4,3    C = 0,0,0,0
i = 1,2,3,4,5    i = 1,2,3,4(A陣列中最大值為4,因此k = 4)
___________________________________________________
A = 4,1,3,4,3    C = 0,0,0,1
    ^
    j            從A[0]開始走訪,發現出現4,因此在C[4]的地方加1,表示4出現過一次
___________________________________________________
A = 4,1,3,4,3    C = 1,0,0,1
      ^
      j
___________________________________________________
A = 4,1,3,4,3    C = 1,0,1,1
        ^
        j
___________________________________________________
A = 4,1,3,4,3    C = 1,0,1,2
          ^
          j
___________________________________________________
A = 4,1,3,4,3    C = 1,0,2,2
            ^
            j
___________________________________________________
接著,為了方便演示,我們產生出一個新的陣列C'
C = 1,0,2,2
C' = 1,0+1,2+0+1,2+2+0+1

陣列C'表示和前一個數字的加總
C' = 1,1,3,5
C'[1]意義為有1個數字小於等於1
C'[2]意義為有1個數字小於等於2
C'[3]意義為有3個數字小於等於3
C'[4]意義為有5個數字小於等於4

C會被C'所取代
___________________________________________________
接著將元素依序放入B,產生出完成排序的陣列
先取出A的最後一個元素A[j],並看到C[A[j]],也就是在計數器中對應的數值,
這個數值表示A[j]在B陣列中的位置,將該數值放到B陣列中
    A = 4,1,3,4,3    C = 1,1,3,5     B = 0,0,3,0,0
index = 1 2 3 4 5        1 2 3 4         1 2 3 4 5
                ^            ^               ^
                j           A[j]          C[A[j]]
___________________________________________________
    A = 4,1,3,4,3    C = 1,1,2,5     B = 0,0,3,0,4
index = 1 2 3 4 5        1 2 3 4         1 2 3 4 5
              ^                ^                 ^
              j               A[j]            C[A[j]]
___________________________________________________
    A = 4,1,3,4,3    C = 1,1,2,4     B = 0,3,3,0,4
index = 1 2 3 4 5        1 2 3 4         1 2 3 4 5
            ^                ^             ^  
            j               A[j]         C[A[j]]
___________________________________________________
    A = 4,1,3,4,3    C = 1,1,1,4     B = 1,3,3,0,4
index = 1 2 3 4 5        1 2 3 4         1 2 3 4 5
          ^              ^               ^
          j             A[j]          C[A[j]]
___________________________________________________
    A = 4,1,3,4,3    C = 0,1,1,4     B = 1,3,3,4,4
index = 1 2 3 4 5        1 2 3 4         1 2 3 4 5
        ^                      ^               ^    
        j                     A[j]          C[A[j]]

得到B,即完成排序。

陣列https://chart.googleapis.com/chart?cht=tx&chl=C的作用基本上是一個計數器,這個計數器告訴我們應該擺放在B的位置,也就是B的index。

https://chart.googleapis.com/chart?cht=tx&chl=C%5B4%5D%20%3D%205來說,表示任何為數值為4的元素他擺放的位置應該要在B的index 5或是更前面的位置。

https://chart.googleapis.com/chart?cht=tx&chl=C%20%3D%20%5Cbegin%7Bbmatrix%7D%201%2C1%2C3%2C5%20%5Cend%7Bbmatrix%7D,這個計數器的意義
如果出現元素數值為1,他所在的位置為B陣列中 index 1或以前的位置
如果出現元素數值為2,他所在的位置為B陣列中 index 1或以前的位置
如果出現元素數值為3,他所在的位置為B陣列中 index 3或以前的位置
如果出現元素數值為4,他所在的位置為B陣列中 index 5或以前的位置

使用C++進行實作

#include <iostream>

using namespace std;
void counting_sort(int *, int *, int, int);
int main(void)
{
    int array_1[5] = {4, 1, 3, 4, 3};
    int *array_2 = (int *)malloc(sizeof(int) * 6);
    counting_sort(array_1, array_2, 4, 5);
    for (int i = 1; i < 6; i++)
    {
        printf("%d ", array_2[i]);
    }
}

void counting_sort(int *A, int *B, int k, int a_lenght)
{
    int *C = (int *)malloc(sizeof(int) * (k + 1)); //index 0 ~ k
    for (int i = 0; i < (k + 1); i++)
    {
        C[i] = 0;
    }
    for (int j = 0; j < a_lenght; j++)
    {
        C[A[j]] = C[A[j]] + 1;
    }
    for (int i = 1; i <= k; i++)
    {
        C[i] = C[i] + C[i - 1];
    }
    for (int j = a_lenght - 1; j >= 0; j--)
    {
        B[C[A[j]]] = A[j];
        C[A[j]] = C[A[j]] - 1;
    }
}

效率分析

在counting_sort主體中,第一個for需要https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(k)的時間,第二個for需要https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(n)的時間(n表示陣列長度),第三個for需要https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(k)的時間,第四個for需要https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(n)的時間,整個counting_sort所需要的時間為https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(n%20%2B%20k),當https://chart.googleapis.com/chart?cht=tx&amp;chl=k%20%3D%20O(n)時,整個counting_sort為https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(n),即為線性時間。

我們得知如果要使counting_sort十分的高效,需要確保陣列中的元素在某一個不會太大的整數範圍內,如果陣列中的數字最多只會到陣列長度https://chart.googleapis.com/chart?cht=tx&amp;chl=n,也就是https://chart.googleapis.com/chart?cht=tx&amp;chl=O(n)的概念,那麼https://chart.googleapis.com/chart?cht=tx&amp;chl=C陣列的長度也會為https://chart.googleapis.com/chart?cht=tx&amp;chl=n,即可實現出線性時間的演算法。

由於我們跳脫出了比較排序的演算法模型,即為元素和元素之間需要比較大小,我們使用計數器去確認元素所需要擺放的位置,因此,counting_sort的時間下界要比比較排序模型的時間下界https://chart.googleapis.com/chart?cht=tx&amp;chl=%5CTheta(nlgn)來得好。

演算法穩定性

演算法穩定性指的是如果在一個待排序的陣列中,有兩個相同的元素,如果在排序後這兩個元素相對位置保持不變,那麼該演算法就是穩定的,像是上面的例子出現了兩個4和兩個3,他們在排序後相對次序皆沒有改變,因此我們可以說counting_sort是穩定的演算法。

參考資料:Introduction to algorithms


上一篇
Day-12 決策樹(decision tree)
下一篇
Day-14 線性時間演算法 : Radix sort
系列文
從0開始啃Introduction of algorithms心得與記錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Alan
iT邦新手 5 級 ‧ 2022-08-16 14:34:18

C = 0,0,2,4 這邊是不是 C = 0,1,1,4才對呀?

對,是 0, 1, 1, 4 這邊筆誤,感謝提醒

j 4:1 1 3 5 
j 3:1 1 2 5 
j 2:1 1 2 4 
j 1:1 1 1 4 
j 0:0 1 1 4 

我要留言

立即登入留言