アルゴリズムチュートリアル:ヒープへのイントロ
提案通り
先週のコメントでは、ヒープの驚くべきプロパティはここで終わらない.調べましょう
heapify
and heapSort
. あなたがヒープ構造に慣れていないならbubbleUp
and trickleDown
それが必要とする操作内容
Heapify
強要する
heapifyは既存の順序のない配列をとる行為を記述し、ヒープ構造に変換します.このプロセスが興味をそそらせるのは、うまく実装されていれば、O(1)のスペースを意味し、O(N)、O(N)、O(N log)時間、O(N log N)時間を意味している.
三つのアプローチ
既存の配列をまとめるには、次の3つの方法をとることができます.
1) We create a new empty heap, and then iterate through the array, inserting each element of the array into this heap using the
insert
andbubbleUp
functions described in the . The drawback to this approach, is that not only are we creating an entirely new structure to hold each value, O(n) space, we also need to iterate through each item in the array, O(n), and insert each one, O(log n), which creates an overall time complexity of O(n log n). It works, but we can do better.
我々のスペース使用を改善するために、我々は既存の配列要素を変更することによってヒープを作成し、必要に応じてこの配列内でそれらをシャフリングする必要がある
bubbleUp()
or trickleDown()
メソッド.2) Our first option would be to start at the first item in the array (root of the tree), and iterate through each element and invoking
bubbleUp
on each one. This would compare each node against its ancestors, and move it further up the tree as necessary. By the time we finish with the last element in the array, each element will have been placed accordingly and we will have our heap.3) The other option, is to start at the bottom-most node on the tree that has children and iterate up to the root, calling
trickleDown
on each one. Like the previous option, this would leave each element correctly place once we finish with the root node.
上記のオプション2と3の効率を比較するために、我々は、与えられたノードのためにどれくらいの潜在的スワップが起こる必要があるかについて見るためにヒープの構造を綿密に調べなければなりません、そして、どのように多くのノードがそれらのスワップをするのに必要でありえたかもしれません.
測定効率
例として15ノードツリーを使いましょう.数学的には、任意のツリー内のタイの数を計算することができます
log n
ここでNはノード数である.この場合、4段である.オプション2のアプローチを使用して、ノードの階層からルートまでの距離を見て、スワップの最悪の合計数を見つけることができました.EX :
n/2 * log n
任意の層に対しては、オプション1のようにO ( n log n )を簡素化しますが、余分なスペースを必要としません.比較のために、我々がオプション3と呼び出しでアプローチを使用するならば
trickleDown
それぞれのノードで、「スワップ数」は16ノードツリーでは非常に異なっているでしょう.EX :
bubbleUp
. 数学的には、このプロセスはO ( n )時間になり、this proof provided by Jeremy West . このプロセスを使用すると、余分なスペースなしで、任意の配列をヒープにできます.実装を高める
heapifyを効率的に実装するには、まず、子ノードを持つツリー内の最後のノードを検索する必要があります
trickleDown
そこからルートへの各々のノードのために.このノードはMath.floor((n - 2)/2)
. 前のブログとは違ってtrickleDown
特定のノードで起動し、常にルートではなく、リファクタリングをしますtrickleDown
以前のポストの実装と比較して任意のパラメータを受け入れる.参照full MaxHeap class gist below のためにtrickleDown
MaxHeapクラスの実装の残りの部分の実装.class MaxHeap {
constructor(arr = []){
this.values = this._heapify(arr)
}
_heapify(arr){
if (this.size > 0) return // Optional: Prevent overriding existing heap values
this.size = arr.length
/**
* To prevent mutating current array, copy arr with
* this.values = [...arr]
*/
this.values = arr
const nodeCount = this.size - 1
// Finds the last node of the tree that has children
let cIdx = Math.floor((nodeCount - 2)/2)
/** For each node up through the root,
* call trickleDown
*/
for (let i = cIdx; i >= 0; i--){
this._trickleDown(i)
}
return this.values
}
// See gist for rest of class implementation
}
適用した場合はヒープインスタンスを生成しますarr = [17,2,36,100,7,1,19,25,3]
私たちはheapify
アクションヒープソート
ヒープソートは、一定のスペースとO(n log n)時間を使用して配列をソートするために上記のHapifyアクションを使用するソーティングメソッドです.このソート方法には本質的に二つの段階があります.
1 )配列を評価する
2 )配列の長さを反復し、各インデックスの最大値をヒープから取り出し、配列の末尾に配置します.
上記のように既に述べたものを利用し、前の投稿からの抽出を利用すると、この動作はかなり似ています.大きな違いは、抽出中に配列から値を削除したくない点です
.pop
, また、毎回、配列の最後のインデックスに抽出値を移動します.代わりに、最大値をどこに配置するかを決定するインデックスポインタを使用することができますtrickleDown
static heapSort(arr){
const heap = new MaxHeap(arr)
for (let i = arr.length - 1; i > 0; i--){
// Place max at pointer position by swapping with root
heap._swap(0,i)
// Begin trickle at root, end before placed value
heap._trickleDown(0, i)
}
return heap.values
}
資源
以下のこれらのリソースは、このポストを置く際に役に立ちました.
クラスハイクラス
< div >
Reference
この問題について(アルゴリズムチュートリアル:ヒープへのイントロ), 我々は、より多くの情報をここで見つけました https://dev.to/dsasse07/algorithm-tutorial-intro-to-heaps-heapify-heap-sort-oa8テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol