クイックソート

概要

クイックソートは、特定の値(ピボット)を基準にして、データの先頭と最後尾から1つずつ中央に近づけていきながら、ピボットより小さなデータが前に、大きなデータが後ろに配置されるように交換し、その処理を再帰的に行っていく。

考え方

以下2段階に分けて、与えられたデータ列をピボット以上・未満に振り分ける操作と、それらを再帰的に実行していく操作を示す。

ピボットの取り方にはいろいろあるようだが、ここでは与えられたデータの先頭と最後尾の値の平均値とする。

部分列に対する操作

以下の図で、4,6,2,…5のデータの部分列が与えられたとする。その際の操作は以下の通り。

  • ⓪ 部分列の先頭と最後尾のデータの平均を計算し、それをピボットとして設定(4.5)
  • ① 左からピボット以上のデータを探索(6≥4.5)、右からピボット未満のデータを探索(1<4.5)
  • ② 探索した左右のデータを交換
  • ③ さらに左からピボット以上のデータを探索(7≥4.5)、右からピボット未満のデータを探索(0<4.5)
  • ④ 探索した左右のデータを交換
  • ⑤ さらに探索を進めた結果、左右のポインターが交差
    • このとき、境界より左にピボット未満、境界より右にピボット以上のデータが格納されている
    • 3を境界とし、境界を含む左側とそれより右側の部分列に分けて、再帰的に探索を行う

再帰処理

部分列の操作を踏まえたうえで、それらの再帰的な処理の様子を下図に示す。図中、それぞれの線種や背景色の意味は以下の通り

  • 二重枠囲みはピボットの値
  • 赤はピボット以上のデータ
  • 青はピボット未満のデータ
  • 黄色は境界値
  • 緑色は位置が確定したデータ

各段階の処理は以下の通り。

  • ① 元の列からピボット以上/未満のデータを振り分け
  • ② 左側の部分列に対する再帰1段目、ピボットによる振り分け
  • ③ 左側の部分列に対する再帰2段目、ピボットによる振り分け
  • ④ 左側の部分列に対する再帰3段目、ピボットによる振り分け
  • ⑤⑥ 左側・右側の再帰終了、0と1の位置確定
  • ⑦ 右側の部分列に対する再帰3段目
  • ⑧⑨ 左側・右側の再帰終了、2と3の位置確定
  • ⑩ 右側の再帰終了、4の位置確定(ここで再帰1段目の左側終了)
  • ⑪ 右側の部分列に対する再帰1段目、ピボットによる振り分け
  • ⑫ 左側の再帰終了、5の位置確定
  • ⑬ 右側の再帰2段目、ピボットによる振り分け
  • ⑭⑮ 左側・右側の再帰終了、6と7の位置確定

コード

以下はPythonによる実装。全体のデータ列に対して位置を指定して並べ替えを実行する再帰関数quick_sort_core()を定義し、全体列に対してこれを呼び出す入口の関数quick_sort()を定義。

例示したデータに対して実行。

要素がすべて同じで、要素数が偶数・奇数の場合も正常に動作。

同じ要素が複数含まれる場合も正常に動作。

動作状況の確認

並べ替えの動作中の状況を以下のコードで確認。各所にprint()関数をちりばめてみる。

動作結果は以下の通りで、再帰的に実行されているのがわかる。