クイックソートとマージソートの両方で、構築される一時サブ配列にO(n)
補助スペースが必要であり、インプレースクイックソートには再帰スタックフレームにO(log n)
補助スペースが必要であることを理解しています。しかし、ヒープソートの場合、ノードが実際の要素への単なるポインタであっても、一時ヒープを構築するためのO(n)
補助スペースの最悪のケースもあるようです。
私はこれに出くわしました 説明 :
ソートする配列内にヒープが構築されるため、O(1)追加スペースのみが必要です。
しかし、これは、元の配列が必ずしも何らかのツリーとして実装されている必要があることを意味すると思いますか?元の配列が単なるベクトルだった場合、ヒープ用のメモリを割り当てる必要があるようです。
配列内のデータは、適切な場所にヒープに再配置できます。このアルゴリズムは実際には驚くほど簡単ですが、ここでは説明しません。
ヒープの並べ替えの場合は、データを配置してヒープを形成し、最小の要素を後ろに配置します(std::make_heap
)。次に、配列の最後の項目(ヒープ内の最小の項目)を配列の最初の項目(大きめの数)と交換し、新しい要素が正しい位置になり、ヒープが再び、配列の最後の要素に残っている最小の要素を持つ新しい最小ヒープ。 (std::pop_heap
)
data: 1 4 7 2 5 8 9 3 6 0
make_heap: [8 7 9 3 4 5 6 2 1 0] <- this is a min-heap, smallest on right
pop_heap(1): [0 7 9 3 4 5 6 2 1 8] <- swap first and last elements
pop_heap(2): 0 [7 9 3 4 8 6 2 5 1] <- shuffle the 8 down the heap
pop_heap(1): 0 1 [9 3 4 8 6 2 5 7] <- swap first and last elements
pop_heap(2): 0 1 [9 7 4 8 6 3 5 2] <- shuffle the 7 down the heap
etc
したがって、実際にはデータを他の場所に格納する必要はありません。ただし、スワップステップ中を除きます。
視覚化のために、標準の形式で表示された元のヒープを次に示します
make_heap
0
2 1
3 4 5 6
8 7 9
pop_heap
8 1 1
2 1 2 8 2 5
3 4 5 6 -> 3 4 5 6 -> 3 4 8 6
7 9 7 9 7 9
ここでのクールなトリックは、ヒープが完全なバイナリツリーであるため、単純な配列を使用でき、アイテムiの場合、その親はアイテムi/2
になります。
ヒープソートはインプレースアルゴリズムです。追加のスペースは必要ありません。要素は、同じ配列内でのみ再帰的に再配置されます。
バイナリヒープまたはツリーが形成されているように見えますが、実際のシナリオでは、ツリーまたはヒープは形成されていません。
HEAP-SORT(A)
{
BUILD-MAX-HEAP(A)
if(i= A.length down to 2)
exchange A[i] with A[1]
A.heapSize = A.heapSize-1
MAX-HEAPIFY(A,1)
}
i/pは、ヒープソートアルゴリズムHEAP-SORT(A)に渡される配列に格納されます。配列Aはツリーとして解釈され、その中からBUILD-MAX-HEAPが出て、最後の要素をルートと交換し、そのたびにヒープサイズを1つ減らしてから、MAX-HEAPIFY(A、1)を呼び出します。
これは、配列(A)内でのみ実行するすべての操作です。これは、アルゴリズムにi/pとして指定されます。この操作の実行中に余分なスペースを使用することはありません。つまり、スペースの複雑さ-O(1)です。