最高の計算の高さをバイナリを検索す(バランス、AVL-tree)
-
05-09-2019 - |
質問
私の計算ノードバランス AVL-ツリー.と思っていたので、その後いくつかの重挿入/更新しい作業ではない正しい(するためのものです。
このような二部、かつ、当初の部品がどのように計算の高さはサブツリーをピックアップしました。定義 "最高のノードの長さは最長の下のウェブサイトへの葉からノードです。" やかだったこともあって、久しぶりの投稿で実施します。とを混乱させるくさらにこの引用できwikipediaにツリーハイツ "従来は、この値は-1に対応するサブツリーノードがないはゼロに対応するサブツリーを一つのノードです。"
第二部でのバランス因子のサブツリーにAVL木々堂々と戦えるならそれでいいの問題を理解するコンセプト "の高さの L
や R
小木および減算 R
から L
".この定義のようなこと: BALANCE = NODE[L][HEIGHT] - NODE[R][HEIGT]
読wikipediaにはこの最初の数行記述に対処するAVL木: "の場合のバランス因子となり、-1、0、1次のツリーはまだAVL形、回転が必要となります。"
そして、こう言って "の場合のバランス因子が2-2その後のツリーに根ざしたこのノードがアンバランスには、ツリーの回転が必要です。最大の単一またはダブルの回転にする必要があるのです。" -ほどの場所に位置していトラブルを把握す.
もりがいが)
こちらで取得しく、テキスト状態 "の場合のバランス率R1できるものであることを挿入が発生した(外部)の右側のノードを左回転が必要".ものかを理解するテキストとしており引用)した場合には、バランス因子以内 [-1, 1]
その必要がないバランス
感じであり把握、コンセプトにしていただき、ツリーの回転、実施が通常のバイナリ検索ツリーの状態を把握すAVL-樹木でも足りないことを必須係。
編集: コード例を優先学術式として私はいつも楽に把握うコードでのお役に立てるようですぞよろしくお願い申し上げます。
編集: そこにいたいほどでしたマークは、全ての答えとして認められた""もっニックの答えしたのとっても行き"aha".
解決
パート1 - 高さ
starblueが言うように、、高さがちょうど再帰的です。擬似コードで:
height(node) = max(height(node.L), height(node.R)) + 1
今の高さは、2つの方法で定義することができます。これは、そのノードへのルートからのパス内のノードの数とすることができる、またはそれはリンクの数とすることができます。あなたがするを参照ページによると、最も一般的な定義は、リンクの数です。その場合には、完全な擬似コードは次のようになります。
height(node):
if node == null:
return -1
else:
return max(height(node.L), height(node.R)) + 1
あなたはノード数を望んでいた場合、コードは次のようになります:
height(node):
if node == null:
return 0
else:
return max(height(node.L), height(node.R)) + 1
いずれかの方法では、私が考えるリバランスアルゴリズムが同じように機能する必要があります。
しかし、あなたのツリーは、あなたが保存してツリーの高さ情報を更新した場合、はるかに効率的(のO(LN(N))の)ことはなく、それをそれぞれの時間を計算します。 ( O(N))
パート2 - バランスをとる
「Rのバランス係数が1であれば、」それは言うとき、、それは上部のバランス率が2であるとき、それが何にするかどうかを選択する方法を言っている、右枝のバランス係数について話しています一回転またはダブル回転。擬似コード(Pythonのような)
if balance factor(top) = 2: // right is imbalanced
if balance factor(R) = 1: //
do a left rotation
else if balance factor(R) = -1:
do a double rotation
else: // must be -2, left is imbalanced
if balance factor(L) = 1: //
do a left rotation
else if balance factor(L) = -1:
do a double rotation
私は、これは理にかなって願っています。
他のヒント
高さを容易に実施する再帰の最大の高さツプラスします。
のバランス因子のR"は右サブツリーをツリーのアンバランスだと思います。
あなたはその場で、ツリーの深さを計算する必要はありません。
あなたが操作を実行すると、あなたはそれらを維持することができます。
さらに、あなたは実際に実際に深さのトラックを維持する必要はありません。あなたは、単純に左と右の木の深さの違いを追跡することができます。
http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_avl.aspx の
は、ちょうど私が回転が面倒くさされた後、バランス係数を整理...
ことを除いて、プログラミングPOVから容易に発見されたバランス係数(左と右のサブツリーの差)を追跡しますここで高さを発見する別の方法です。あなたのノードと呼ばれる高さに追加の属性を追加します:
class Node
{
data value; //data is a custom data type
node right;
node left;
int height;
}
さて、私たちは木のシンプルな幅優先トラバーサルを行うには、各ノードの高さの値を更新しておこう。
int height (Node root)
{
Queue<Node> q = Queue<Node>();
Node lastnode;
//reset height
root.height = 0;
q.Enqueue(root);
while(q.Count > 0)
{
lastnode = q.Dequeue();
if (lastnode.left != null){
lastnode.left.height = lastnode.height + 1;
q.Enqueue(lastnode.left);
}
if (lastnode.right != null){
lastnode.right.height = lastnode.height + 1;
q.Enqueue(lastnode.right);
}
}
return lastnode.height; //this will return a 0-based height, so just a root has a height of 0
}
乾杯、
さて、あなたは以下の再帰関数で木の高さを計算することができます:
int height(struct tree *t) {
if (t == NULL)
return 0;
else
return max(height(t->left), height(t->right)) + 1;
}
max()
とstruct tree
の適切な定義を持ちます。あなたは、これはあなたが引用経路長に基づいて定義に対応する理由を把握するために時間を取る必要があります。この関数は空の木の高さとしてゼロを使用します。
しかし、AVL木のようなもののために、私はあなたが実際に高さにあなたがそれを必要とするたびに計算するとは思いません。代わりに、各ツリーノードは、そのノードをルートとするサブツリーの高さを覚えて、余分なフィールドで増補されます。このフィールドは、ツリーが挿入および欠失によって変更されるため、最新の状態に保つ必要がある。
私はAVL木の形状が正確になりますが、それは期待対数の性能を持っていないことを、上記提案のように。
あなたの代わりに、ツリー内でのキャッシングの高さを毎回計算する場合は、と思われますここでは混乱を取得ここで、テキストは「Rのバランス係数が1である場合は、挿入は、そのノードの(外部の)右側に発生し、左回転が必要とされる手段」状態です。しかし、テキストを理解メートルからバランス係数が以内であれば[-1、1]その後、バランスをとるためには必要がなかったこと(私が引用した)言った?
R
は、現在のノードN
の右側の子である。
はbalance(N) = +2
場合は、ある種の回転を必要とします。しかし、これは回転を使用するには?まあ、それはbalance(R)
によって異なりますbalance(R) = +1
は、あなたがN
上の左回転が必要な場合は、 balance(R) = -1
場合は、あなたはいくつかの種類の二重回転が必要になります。
Rのバランス係数が1である場合は、それは混乱なるのはここだ、テキストはそれは、」述べて 挿入は、そのノードの(外部の)右側と左側に発生手段 回転は(私が引用した)場合は、その」必要としていました。しかし、テキストを理解メートルから言われています バランス係数は、均衡の必要はありませんでした[1、-1]の範囲内でしたか?
さて、ひらめき時間ます。
回転が何を考えてみましょう。さんは左回転について考えてみましょう。
P = parent
O = ourself (the element we're rotating)
RC = right child
LC = left child (of the right child, not of ourself)
P
\
O
\
RC
/
LC
P
\
RC
/
O
\
LC
10
\
15
\
20
/
18
10
\
20
/
15
\
18
basically, what happens is;
1. our right child moves into our position
2. we become the left child of our right child
3. our right child's left child becomes our right
さて、あなたはここで気づくしなければならない大きなもの - この左回転がTREEの深さを変更していません。我々はそれを行ったためのよりバランスのとれたじゃないんだ。
しかし - と、ここAVLで魔法だ - 私たちは右FIRSTに右の子を回転させた場合、私たちが持っているだろうことはこれです...
P
\
O
\
LC
\
RC
そして今、我々は、Oは私たちが得ることで、左回転させる場合は、この...
P
\
LC
/ \
O RC
マジック!私たちは、ツリーのレベルを取り除くことができた - の私たちは木のバランスのを作りました。
。より完全上位レベルを超える深さを退治木手段バランシング、および梱包 - 我々だけやった、まさにである
。シングル/ダブルローテーションについてその全体のものは、あなたのサブツリーはこのように見ていなければならないことだけである。
P
\
O
\
LC
\
RC
あなたが回転する前に - あなたはその状態に入るために右回転を行う必要があります。あなたがその状態で、すでにしている場合でも、あなただけ左ローテートを行う必要があります。
そのコンストラクタで0に初期化さBinaryTree<T, Comparator>::Node
データメンバを、subtreeHeight
与える、としてたびに自動的に更新します:
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setLeft (std::shared_ptr<Node>& node) {
const std::size_t formerLeftSubtreeSize = left ? left->subtreeSize : 0;
left = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize++;
node->depthFromRoot = depthFromRoot + 1;
const std::size_t h = node->subtreeHeight;
if (right)
subtreeHeight = std::max (right->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerLeftSubtreeSize;
subtreeHeight = right ? right->subtreeHeight + 1 : 0;
}
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setRight (std::shared_ptr<Node>& node) {
const std::size_t formerRightSubtreeSize = right ? right->subtreeSize : 0;
right = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize++;
node->depthFromRoot = depthFromRoot + 1;
const std::size_t h = node->subtreeHeight;
if (left)
subtreeHeight = std::max (left->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerRightSubtreeSize;
subtreeHeight = left ? left->subtreeHeight + 1 : 0;
}
}
そのデータメンバのsubtreeSize
とdepthFromRoot
も更新されます。
ノードを挿入する場合、これらの機能は、例えば、(すべてを試験)と呼ばれています。
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node>
BinaryTree<T, Comparator>::Node::insert (BinaryTree& tree, const T& t, std::shared_ptr<Node>& node) {
if (!node) {
std::shared_ptr<Node> newNode = std::make_shared<Node>(tree, t);
node = newNode;
return newNode;
}
if (getComparator()(t, node->value)) {
std::shared_ptr<Node> newLeft = insert(tree, t, node->left);
node->setLeft(newLeft);
}
else {
std::shared_ptr<Node> newRight = insert(tree, t, node->right);
node->setRight(newRight);
}
return node;
}
ノードを削除する場合、removeLeft
とremoveRight
を置き換えることによってsubtreeSize++;
とsubtreeSize--;
の異なるバージョンを使用します。 rotateLeft
とrotateRight
のためのアルゴリズムは、いずれかの非常に問題なく適合させることができます。以下は、テストされ、可決された。
template <typename T, typename Comparator>
void BinaryTree<T, Comparator>::rotateLeft (std::shared_ptr<Node>& node) { // The root of the rotation is 'node', and its right child is the pivot of the rotation. The pivot will rotate counter-clockwise and become the new parent of 'node'.
std::shared_ptr<Node> pivot = node->right;
pivot->subtreeSize = node->subtreeSize;
pivot->depthFromRoot--;
node->subtreeSize--; // Since 'pivot' will no longer be in the subtree rooted at 'node'.
const std::size_t a = pivot->left ? pivot->left->subtreeHeight + 1 : 0; // Need to establish node->heightOfSubtree before pivot->heightOfSubtree is established, since pivot->heightOfSubtree depends on it.
node->subtreeHeight = node->left ? std::max(a, node->left->subtreeHeight + 1) : std::max<std::size_t>(a,1);
if (pivot->right) {
node->subtreeSize -= pivot->right->subtreeSize; // The subtree rooted at 'node' loses the subtree rooted at pivot->right.
pivot->subtreeHeight = std::max (pivot->right->subtreeHeight, node->subtreeHeight) + 1;
}
else
pivot->subtreeHeight = node->subtreeHeight + 1;
node->depthFromRoot++;
decreaseDepthFromRoot(pivot->right); // Recursive call for the entire subtree rooted at pivot->right.
increaseDepthFromRoot(node->left); // Recursive call for the entire subtree rooted at node->left.
pivot->parent = node->parent;
if (pivot->parent) { // pivot's new parent will be its former grandparent, which is not nullptr, so the grandparent must be updated with a new left or right child (depending on whether 'node' was its left or right child).
if (pivot->parent->left == node)
pivot->parent->left = pivot;
else
pivot->parent->right = pivot;
}
node->setRightSimple(pivot->left); // Since pivot->left->value is less than pivot->value but greater than node->value. We use the NoSizeAdjustment version because the 'subtreeSize' values of 'node' and 'pivot' are correct already.
pivot->setLeftSimple(node);
if (node == root) {
root = pivot;
root->parent = nullptr;
}
}
ここで、
inline void decreaseDepthFromRoot (std::shared_ptr<Node>& node) {adjustDepthFromRoot(node, -1);}
inline void increaseDepthFromRoot (std::shared_ptr<Node>& node) {adjustDepthFromRoot(node, 1);}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::adjustDepthFromRoot (std::shared_ptr<Node>& node, int adjustment) {
if (!node)
return;
node->depthFromRoot += adjustment;
adjustDepthFromRoot (node->left, adjustment);
adjustDepthFromRoot (node->right, adjustment);
}
ここで、コード全体である: http://ideone.com/d6arrv の
このBFS-のようなソリューションは非常に簡単です。単純にレベルが1対1にジャンプします。
def getHeight(self,root, method='links'):
c_node = root
cur_lvl_nodes = [root]
nxt_lvl_nodes = []
height = {'links': -1, 'nodes': 0}[method]
while(cur_lvl_nodes or nxt_lvl_nodes):
for c_node in cur_lvl_nodes:
for n_node in filter(lambda x: x is not None, [c_node.left, c_node.right]):
nxt_lvl_nodes.append(n_node)
cur_lvl_nodes = nxt_lvl_nodes
nxt_lvl_nodes = []
height += 1
return height