カウントソート

Harshit Jindal 2023年10月12日
  1. カウントソートアルゴリズム
  2. カウントソートの例
  3. カウントソートアルゴリズムの実装
  4. 数え並べ替えアルゴリズムの複雑さ
カウントソート

カウントソートは、非比較ソートアルゴリズムです。各ユニークな要素の出現をカウントすることで配列をソートします。一意な要素のカウントを部分的にハッシュ化し、最終的にソートされた配列の各要素のインデックスを見つけるために計算を行います。非常に高速なアルゴリズムですが、大規模なデータセットには不向きです.[radix sort](/ja/tutorial/algorithm/radix-sort/内のサブルーチンとして使用されます。

カウントソートアルゴリズム

ここでは、n 個の要素を含むソートされていない配列 A[] があるとします。

  • 配列内の要素の最大値 max と最小値 min を求める。
  • すべての要素を 0 に設定した長さ max-min+1 の配列 count を初期化します。
  • 入力配列 A と同じサイズの配列 output を初期化します。
  • 配列 count から min を引き、その差をインデックスにして、配列 count 内の全要素のカウントを格納します。
  • count 配列内の要素の合計を計算します。これで count 配列は、ソートされた配列の各要素の位置を保持するようになります。
  • 最後から A から要素を取り出し、以下のようにします。
  • 要素のインデックスを count[A[i]-min]-1 として計算し、A[i]output に格納します。
  • count[A[i]-min]1 減らす。
  • output の要素を元の配列 A に戻す。

カウントソートの例

配列があるとしましょう。(6, 3, 2, 7, 4, 5, 4, 2). これをカウントソートアルゴリズムを用いてソートします。

  • maxm = 7
  • minm = 2
  • 範囲 range = 6
  • countoutput は 0 で初期化されています。
インデックス 0 1 2 3 4 5
値打ち 0 0 0 0 0 0
  • すべての要素のカウントを計算した後に count 配列を作成します。
インデックス 0 1 2 3 4 5
値打ち 2 1 2 1 1 1
  • 要素の合計を累積した後の count 配列。
インデックス 0 1 2 3 4 5
値打ち 2 3 5 6 7 8
  • 最初の繰り返し: A[7]=2.
count 1 3 5 6 7 8
output 0 2 0 0 0 0 0 0
  • 2 回目の反復: A[6]=4.
count 1 3 4 6 7 8
output 0 2 0 0 4 0 0 0
  • 3 回目の反復: A[5]=5.
count 1 3 4 5 7 8
output 0 2 0 0 4 5 0 0
  • 第 4 反復: A[4]=4.
count 1 3 3 5 7 8
output 0 2 0 4 4 5 0 0
  • 第 5 反復: A[3]=7.
count 1 3 3 5 7 7
output 0 2 0 4 4 5 0 7
  • 第 6 反復: A[2]=2.
count 0 3 3 5 7 7
output 2 2 0 4 4 5 0 7
  • 7 回目の反復: A[1]=3.
count 0 2 3 5 7 7
output 2 2 3 4 4 5 0 7
  • 8 回目の反復: A[0]=6.
count 0 2 3 5 6 7
output 2 2 3 4 4 5 6 7

最後に、配列 outputA 内に格納し、ソートされた配列 2, 2, 3, 4, 4, 5, 6, 7 を取得します。

カウントソートアルゴリズムの実装

#include <iostream>
using namespace std;
const int N = 10;

int maxm(int arr[], int n) {
  int max = arr[0];
  for (int i = 1; i < n; i++) {
    if (arr[i] > max) {
      max = arr[i];
    }
  }
  return max;
}

int minm(int arr[], int n) {
  int min = arr[0];
  for (int i = 1; i < n; i++) {
    if (arr[i] < min) {
      min = arr[i];
    }
  }
  return min;
}

void countingSort(int arr[], int n) {
  int min = minm(arr, n);
  int max = maxm(arr, n);
  int range = max - min + 1;
  int count[range] = {};
  int output[n];
  for (int i = 0; i < n; i++) count[arr[i] - min]++;

  for (int i = 1; i < range; i++) count[i] += count[i - 1];

  for (int i = n - 1; i >= 0; i--) {
    output[count[arr[i] - min] - 1] = arr[i];
    count[arr[i] - min]--;
  }

  for (int i = 0; i < n; i++) arr[i] = output[i];
}

int main() {
  int n = 7;
  int arr[7] = {3, 5, 1, 1, 4, 2, 1};
  cout << "Input arr: ";
  for (int i = 0; i < n; i++) {
    cout << arr[i] << " ";
  }
  cout << "\n";
  countingSort(arr, n);  // Sort elements in ascending order
  cout << "Output arr: ";
  for (int i = 0; i < n; i++) {
    cout << arr[i] << " ";
  }
  cout << "\n";
}

この実装は負の数に対しても動作します。

数え並べ替えアルゴリズムの複雑さ

時間計算量

  • 平均ケース

カウントソートは、n のすべての項目を 2 回処理し、count の配列を 1 回処理します。したがって、時間的複雑度は O(n + b) であり、b は入力の範囲です。b は小さいことが多いので、カウントソートの時間的複雑さは [Big Theta]: O(n) のオーダーと言われています。

  • 最悪の場合

最悪の場合の時間的複雑さは [Big O]: O(n) です。

  • 最悪の場合

最良の時間複雑度は[ビッグオメガ]: O(n) です。これは最悪の時間複雑度と同じです。

空間計算量

カウントソートアルゴリズムの空間複雑度は O(n+b) であり、b は入力の範囲です。これは countoutput の配列から来ています。ときには bn よりも大きくなることもあるが、b が小さい場合、時間的複雑さは O(n) となると言われています。

著者: Harshit Jindal
Harshit Jindal avatar Harshit Jindal avatar

Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.

LinkedIn

関連記事 - Sort Algorithm