Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

3-Wayクイックソート(オランダ国旗問題)とは?アルゴリズムとC++実装をわかりやすく解説


本記事では、クイックソートの改良版である「3-Wayクイックソート(スリーウェイクイックソート)」について詳しく解説します。基本的なクイックソートでは、まずピボット(基準値)となる要素を1つ選び、そのピボットを境に配列を分割します。その後、ピボットの左側と右側にある部分配列それぞれに対して、再帰的に同じ処理を繰り返すことで全体を整列させます。

一方、3-Wayクイックソートも同様の考え方に基づいていますが、配列を3つの領域に分割する点が大きな特徴です。配列 arr[1 to n] は以下のように分けられます。

  • arr[1 to i]:ピボットより小さい要素の集まり
  • arr[i + 1, j]:ピボットと等しい要素の集まり
  • arr[j + 1, n]:ピボットより大きい要素の集まり

この分割手法は「オランダ国旗(Dutch National Flag)問題」としても知られており、特に同じ値が大量に含まれるデータを扱う場合に、通常のクイックソートよりも効率よく動作するという利点があります。

アルゴリズム

まず、partition(パーティション:分割)関数の擬似コードを見てみましょう。

begin
   if right – left <= 1, then
      if arr[right] < arr[left], then
         swap arr[right] and arr[left]
      i := left
      j := right
      return
   end if
   mid := left, pivot = arr[right]
   while mid <= right, do
      if arr[mid] < pivot, then
         swap arr[left], arr[mid]
         increase left and mid by 1
      else if arr[mid] = pivot, then increase mid by 1
      else
         swap arr[mid], arr[right]
         decrease right by 1
   done
   i := left – 1
   j := mid
end

次に、quicksort関数の擬似コードです。

begin
   if left >= right, then
      return
   end if
   define i and j
   partition(arr, left, right, i, j)
   quicksort(arr, left, i)
   quicksort(arr, j, right)
end

C++による実装例

それでは、上記のアルゴリズムをC++で実装したサンプルコードを紹介します。

#include<iostream>
#include<vector>
using namespace std;
// 配列を3つの領域に分割する関数
void partition(int arr[], int left, int right, int &i, int &j) {
   // 要素数が2個以下の場合
   if (right - left <= 1) {
      // 順序が逆なら交換
      if (arr[right] < arr[left])
         swap(arr[right], arr[left]);
      i = left;
      j = right;
      return;
}
int mid = left;
int pivot = arr[right];
while (mid <= right) {
   // ピボット未満 → 左端へ移動
   if (arr[mid]<pivot)
      swap(arr[left++], arr[mid++]);
      // ピボットと等価 → そのまま次へ
      else if (arr[mid]==pivot)
         mid++;
      // ピボット超過 → 右端へ移動
      else if (arr[mid] > pivot)
         swap(arr[mid], arr[right--]);
   }
   i = left-1;
   j = mid;
}
// 3-Wayクイックソート本体
void quicksort(int arr[], int left, int right) {
   if (left >= right) // 要素数が1または0の場合は終了
      return;
   int i, j;
   partition(arr, left, right, i, j);
   quicksort(arr, left, i);
   quicksort(arr, j, right);
}
// 配列の内容を表示する関数
void display(int arr[], int n) {
   for (int i = 0; i < n; ++i)
   cout << " " << arr[i];
   cout << endl;
}
int main() {
   int a[] = {4, 9, 4, 3, 1, 9, 4, 3, 9, 4, 3, 1, 4};
   int n = sizeof(a) / sizeof(int);
   display(a, n);
   quicksort(a, 0, n - 1);
   display(a, n);
}

出力結果

プログラムを実行すると、以下のように整列前後の配列が出力されます。

4 9 4 3 1 9 4 3 9 4 3 1 4
1 1 3 3 3 4 4 4 4 4 9 9 9

重複値(この例では「4」や「9」)が多い入力でも、3-Wayクイックソートは等しい要素を一括して中央の領域へまとめるため、無駄な比較・交換を減らし、効率よくソートできていることが確認できます。


  1. C言語で配列を左にn回転させるプログラムの書き方

    C言語で配列を左方向にn回転させるプログラムの書き方を解説します。配列の要素を指定した回数だけ左へシフトし、はみ出した先頭の要素を末尾に移動させる「左回転(左ローテート)」の基本的なロジックを、サンプルコードと実行例付きでわかりやすく紹介します。入力と出力の例入力: arr[] = 1 2 3 4 5 6 7 8 9 10N = 3出力: 4 5 6 7 8 9 10 1 2 3アルゴリズムの流れまず、配列 arr に要素を格納します。回転させる回数を変数 N に設定します。「1回分の左回転」をN回繰り返します。1回分の左回転とは、配列の全要素を1つずつ左にずらし、先頭にあった要素を末尾へコピ

  2. Pythonでクイックソートを実装する方法|初心者向けにサンプルコードを徹底解説

    この記事では、クイックソート(QuickSort)のアルゴリズムを使って配列を並べ替えるPythonプログラムの実装方法を、初心者にもわかりやすく解説します。 問題の定義 問題: 与えられた配列を、クイックソートの考え方を利用して昇順にソートすることです。 クイックソートは「分割統治法」と呼ばれる手法に基づく高速なソートアルゴリズムです。まず配列を基準値(ピボット)を境目に2つの部分に分割し、それぞれの部分配列を再帰的にソートしていくことで、最終的に全体が整列された配列を得られます。 クイックソートの仕組み 処理の流れは以下のとおりです。 配列からピボット(基準となる要素)を選びます。こ