私はAVLサーチツリーの実装に取り組んでいます。これまでのところ、コーディング部分を終了し、バグのテストを開始しました。ノードのローテーション方法にバグがあることがわかりました。神のために、何が問題なのか理解できません。
アルゴリズムは紙の上では正常に機能しますが、マシン上で実行するとうまく機能します...ツリーノードをリークします。
これは、ノードを左に回転させるために使用される方法です:http: //pastebin.com/mPHj29Af
bool avl_search_tree::avl_tree_node::rotate_left()
{
if (_right_child != NULL) {
avl_tree_node *new_root = _right_child;
if (_parent != NULL) {
if (_parent->_left_child == this) {
_parent->_left_child = new_root;
} else {
_parent->_right_child = new_root;
}
}
new_root->_parent = _parent;
_parent = new_root;
_right_child = new_root->_left_child;
new_root->_left_child = this;
if (_right_child != NULL) {
_right_child->_parent = this;
}
//update heights
update_height();
new_root->update_height();
return true;
}
return false;
}
私の挿入方法では、AVLバランシング部分にコメントを付けましたが、代わりに、新しく挿入されたノードを左に回転させようとしています。整数を昇順で挿入した結果:私のツリーには最初のルート(最初に挿入されたノード)のみが含まれ、他のすべてのノードがリークされます。
私が怒り始めているので、問題を特定するのにどんな助けでも大いに感謝されます。
記録のために:回転を使用しない場合、ツリーはノードをリークせず、通常の不平衡二分探索ツリーとして機能します(挿入とルックアップ用)。
編集:AJG85のコメントにより、私は観察を追加します:
クリーンアップの前にキー値(私の場合は32ビット整数)を出力するavl_search_tree :: avl_tree_nodeのデストラクタメソッドと、挿入されたばかりのキーを出力するavl_search_treeのinsertメソッドにprintf'checks'を追加しました。
次に、プログラムのエントリポイントで、ヒープにavl_search_treeを割り当て、昇順でキーを追加してから削除します。
AVLバランシングを有効にすると、ターミナルに次の出力が表示されます。
bool avl_search_tree::insert(const int&) : 1
bool avl_search_tree::insert(const int&) : 2
bool avl_search_tree::insert(const int&) : 3
bool avl_search_tree::insert(const int&) : 4
bool avl_search_tree::insert(const int&) : 5
bool avl_search_tree::insert(const int&) : 6
bool avl_search_tree::insert(const int&) : 7
bool avl_search_tree::insert(const int&) : 8
avl_search_tree::avl_tree_node::~avl_tree_node() : 1
これは、すべての挿入が成功したが、ルートのみが削除されたことを意味します。
AVL Balancingがコメントアウトされているので、通常の二分探索木のように機能します。端末出力は次のとおりです。
bool avl_search_tree::insert(const int&) : 1
bool avl_search_tree::insert(const int&) : 2
bool avl_search_tree::insert(const int&) : 3
bool avl_search_tree::insert(const int&) : 4
bool avl_search_tree::insert(const int&) : 5
bool avl_search_tree::insert(const int&) : 6
bool avl_search_tree::insert(const int&) : 7
bool avl_search_tree::insert(const int&) : 8
avl_search_tree::avl_tree_node::~avl_tree_node() : 1
avl_search_tree::avl_tree_node::~avl_tree_node() : 2
avl_search_tree::avl_tree_node::~avl_tree_node() : 3
avl_search_tree::avl_tree_node::~avl_tree_node() : 4
avl_search_tree::avl_tree_node::~avl_tree_node() : 5
avl_search_tree::avl_tree_node::~avl_tree_node() : 6
avl_search_tree::avl_tree_node::~avl_tree_node() : 7
avl_search_tree::avl_tree_node::~avl_tree_node() : 8
これは、すべてが適切にクリーンアップされることを意味します。
さて...どのようにして回転方法が問題であるという結論に達しましたか?コメント付きのAVLバランシングサブルーチンの下に、新しく挿入されたすべてのノードを左に回転させる行を追加しました。結果?AVLバランシングサブルーチンが有効になっている場合と同じです。
また、update_height()メソッドに関しては、ツリーの構造を変更することはありません。
これで明らかになることを願っています。
編集2:
さらにいくつかのことを明確にするために、彼はavl_tree_nodeデストラクタがどのように実装されているかを示しています。
avl_search_tree::avl_tree_node::~avl_tree_node()
{
printf("%s : %d\n", __PRETTY_FUNCTION__, *_key);
if (_left_child != NULL) {
delete _left_child;
}
if (_right_child != NULL) {
delete _right_child;
}
if (_key != NULL) {
delete _key;
}
}
_left_childと_right_childは、ヒープに割り当てられたavl_tree_nodeオブジェクトへのポインターです。
編集3:
AGJ85の2番目のコメントのおかげで、私は問題を見つけました。私のrotateメソッドでは、ルートがシフトされるたびに、ツリーのルートポインタを新しいルートに更新する必要があることを忘れていました。
基本的に、ツリーのルートは常に最初に挿入されたノードを指しており、必要に応じてポインターを更新しなければ、rotateメソッドは実際に正しく構成された新しいツリーのルートをリークしていました。:)
AGJ85ありがとうございます!