ヒープソートで配列を並び替える
ヒープソートを使用する
ヒープソート (heap sort) は、配列を 二分ヒープ の形に整え、その根に常に最大値が来る性質を利用して、根と末尾を入れ替えて確定させながらヒープを縮めていく インプレース の比較ソートである。
- 準備: 配列をインデックス付きの完全二分木とみなす。親
i(i >= 0) に対し左の子は2i+1、右の子は2i+2になる。 - 構築: 親の値が子の値以上という条件を満たすよう、葉に近い内部ノードから順に沈降して 最大ヒープ にする。
- 抽出: 根(位置
0)が全体の最大なので、それと ヒープの末尾 を交換する。末尾側はもうソート済み領域として固定し、残り[0 .. heapEnd-1]だけをヒープとして見なす。 - 反復: 新しい根がヒープ条件を満たすまで子と比較・交換して下へ沈める。ヒープ長が 1 になるまで手順 3〜4 を繰り返す。
procedure heap_sort(A)
n = length(A)
for i from floor(n / 2) - 1 down to 0
sift_down(A, i, n)
for end from n - 1 down to 1
swap(A[0], A[end])
sift_down(A, 0, end)
procedure sift_down(A, start, heap_end)
i = start
while true
l = 2 * i + 1
r = 2 * i + 2
largest = i
if l < heap_end and A[l] > A[largest] then
largest = l
if r < heap_end and A[r] > A[largest] then
largest = r
if largest = i then
break
swap(A[i], A[largest])
i = largest
最悪・平均・最良いずれも時間計算量は O(n log n) で、追加の配列を使わない標準実装では 空間計算量は O(1)(再帰を使わない実装が一般的)。同一キーの相対順序を保たない 不安定 なソートであることが多い。
バブルソートの O(n²) と比べて大きな入力では優位になりやすく、クイックソートと異なり 最悪でも O(n log n) に抑えられる反面、定数倍やキャッシュ効率では実装やデータに依存してクイックソートに劣ることもある。
優先度付きキューの内部にもヒープが使われるため、アルゴリズムの理解はヒープソートだけでなく広い実装の読み取りにもつながる。