Red Black Treeの実装


Red BlackTreeとは?

  • 各ノードはredノードまたはblackノードである.
  • のノードは黒いノードです.
  • すべてのリーフノード(実際の内部ノードではなくnilノード)は黒いノードです.
  • 赤ノードの子供はblackです.すなわちredノードは連続的に現れない.
  • のすべてのノードについて、そのノードからリーフノードまでのブラックノードの数は同じである.
  • 2.なぜRedBlackTreeが必要なのか

  • RedBlackTreeは、ツリー内のリーフノードのレベルが大きく異なることを防止するためのデータ構造です.
  • のように配列された線形資料構造では、閲覧、削除、挿入などo(n)時間を有するのとは異なり、データをツリー形式で格納し、閲覧、削除、挿入などの操作においてo(logn)の時間複雑度を有するデータを保持する.
  • しかし、このようなデュアルナビゲーションツリーは、リーフノードのレベルが異なる場合、時間的複雑度がo(n)に近い可能性があるため、各リーフノードのレベルを統一的に調整する必要がある.
  • のデータ構造は、AVLツリー、RedBlackTree、多くのリレーショナル・データベース・インデックスで使用されるB+ツリーなどを含む.
  • プログラミング言語では、Set、Mapなどのデータ構造はHashTableを採用するか、RedBlackTreeを採用する.
  • 3. RedBlackTree vs AVL Tree

  • AVLツリーはリーフノードレベルをより厳格に調整するため、検索速度が速くなります.
  • を挿入および削除すると、RedBlackTreeは構造を再調整する際により少ない演算を行うため、より高速になります.
  • 各ノードの情報はAVLツリーでより多くなるため、メモリの使用においてRedBlackTreeがより効果的である.
  • そのため、プログラミング言語のデータ構造では主にRed BlackTreeが用いられ、ナビゲーション速度が重要で記憶容量が十分なデータベースではAVL Treeが用いられることが多い.
  • 4.n個の内部ノードを有する赤黒木の高さは2 log(2)(n+1)以下である。


    ブラックノードの高さ:私のサブノード(nilノードを含む)のブラックノードの数.
  • 証明
  • 黒いノードの高さは、常にノード全体の高さの半分以上です.
    bh >= h/2
    ->赤のノードが連続していないため、黒のノードが常に多いか同じであるのは当然です.
  • あるノードの黒ノードの高さがbhの場合、2つのサブツリーは2^bh−1の最小の内部ノードを有する.
    ->ブラックノードのみの場合が最も少ない.
  • 1,2を組み合わせ、n個の内部ノードを有する赤黒樹の高さは2 log(2)(n+1)以下である.
    ->n >= 2^bh - 1 >= 2^(h/2) - 1
  • 5.Red Black Tree計算の実施

  • insert,delete後に赤黒樹構造を維持することがコアである.
  • 1)left-rotate vs right-rotate

  • o(1)の時間的複雑さ;バイナリナビゲーションツリー構造を維持する演算
  • insert、削除後に破損した赤と黒の木を復元します.
  • 2)insert

  • insertは常にredノード
  • を入力する.
  • 両問題状況
  • 入力ノードがルートノードの場合->簡単に処理できる
  • 親ノードが赤ノードの場合(red-red違反)->2つの連続した赤をルートノードに移動します.この場合は3つのケースがある.
  • (1)case 1:おじさんノードが赤いノードであれば


  • 両親とおじさんはノードが黒で、おじいさんは赤(両親が赤なのでおじいさんは黒でなければなりません)

  • red-red違反は2つのノードに移行します

  • (2)case 2:おじさんノードは黒,他のノードは右サブノード

  • 追加ノードの親ノードの場合、左に曲がるとcase 3になります.
  • 左-回転後も赤い黒い木が維持されます.
  • (3)case 3:おじさんノードが黒いノード、その他のノードが左側のサブノード

  • 親が黒くなり、おじいさんが赤くなり、おじいさんのノードを右-回転します.
  • アルファ、β、ガンマ、デルタは同じ黒い高さを持っています.
  • red-red違反一括解決
  • iterator insert(iterator position, const value_type& value){
    			node_pointer new_node = search(value,_root);
    			if (new_node)
    				return (iterator(new_node));
    			new_node = _node_alloc.allocate(1);
    			_node_alloc.construct(new_node, Node<value_type>(create_value(value)));
    			new_node->left = _nil;
    			new_node->right = _nil;
    			if (position == begin()){
    				if (position != end() && _compare(value, *position))
    					_insert_into_tree(new_node, tree_min(_root));
    				else
    					_insert_into_tree(new_node, _root);
    			}
    			else if (position == end()){
    				if (position != begin() && _compare(*(--position), value))
    					_insert_into_tree(new_node, _header->parent);
    				else
    					_insert_into_tree(new_node, _root);
    			}
    			else
    				_insert_into_tree(new_node, _root);
    			_insert_fixup(new_node);
    			_size++;
    			node_pointer max_of_tree = tree_max(_root);
    			max_of_tree->right = _header;
    			_header->parent = max_of_tree;
    			return (iterator(new_node));
    		}
    
    void _insert_fixup(node_pointer node){
    			if (node != _root && node->parent != _root){
    				while (node != _root && !node->parent->is_black){
    					if (node->parent == node->parent->parent->left){
    						node_pointer uncle = node->parent->parent->right;
    						if (!uncle->is_black){
    							node->parent->is_black = true;
    							uncle->is_black = true;
    							node->parent->parent->is_black = false;
    							node = node->parent->parent;
    						}
    						else {
    							if (node == node->parent->right){
    								node = node->parent;
    								_rotate_left(node);
    							}
    							node->parent->is_black = true;
    							node->parent->parent->is_black = false;
    							_rotate_right(node->parent->parent);
    						}
    					}
    					else{
    						node_pointer uncle = node->parent->parent->left;
    						if (!uncle->is_black){
    							node->parent->is_black = true;
    							uncle->is_black = true;
    							node->parent->parent->is_black = false;
    							node = node->parent->parent;
    						}
    						else{
    							if (node == node->parent->left){
    								node = node->parent;
    								_rotate_right(node);
    							}
    							node->parent->is_black = true;
    							node->parent->parent->is_black = false;
    							_rotate_left(node->parent->parent);
    						}
    					}
    				}
    			}
    			_root->is_black = true;
    		}
    

    3)delete



    y削除するノード、xはサブノード(なしまたは1つのみ)

    (1)削除するノードが赤の場合は、通常バイナリツリーを削除する場合と同様に削除できます。

  • 削除するノードのサブノードがありません
    ->直接削除
  • ノードのサブノードが1つある場合、
  • を削除します.
    ->削除ノードの親ノード
  • に子ノードを接続する
  • には、削除するノードのサブノードが2つあります.
    ->右側のサブアイテムの最小値を削除します.
    ->右側のサブノードの最小値を持つノードを削除します.サブノードが1つある場合は削除します.
  • (2)削除するノードが黒の場合はredblack tree delete fixが必要

  • yはルートノードであり、上のxは赤色である
    ->xを黒に変更すると修復されます.
  • を削除するノードが黒の場合、サブツリー内のすべてのノードの黒の高さは1だけ減少します.
    ->削除ノード内の一意のサブノード(x)にblackプロパティを追加します.
    ->double-blackor red-blackになり、red-blackならblackになります.
    ->double-backの場合、blackを親に昇格させる論理を実行します.
  • y,xがdouble-backの場合を除いて4つの場合に分けられる.
    しかし、兄弟ノードwはnilノードではない.
    case 1:wは赤
  • xの両親に対して、左に曲がってcase 2,3,4の1つになります.
  • Case 2:wはblack、wサブノードもblackノード
  • xとwはblackを両親に渡し、wはred、xはblackです.両親は追加のblackを手に入れます.
  • Case 3:wは黒、w左側のサブノードは赤、右側のサブノードは黒
  • wの場合、右回転とblack heightを調整するために、wとwの左側のサブノードの色を変更します.case 4になりました.
  • case 4:wはblack,w右のサブノードはredノード,右のサブノードはunknown
  • xの親に左回転、wは赤ノード、元のxの親にxの追加黒->を適用すべてのパスについてblack heightは以前と同じままです.
  • ケースは次のように行います.
    void erase(iterator pos){
    			node_pointer y = pos.node(), x, for_free = y;
    			bool y_original_is_black = y->is_black;
    			if (is_nil(y->left)){
    				x = y->right;
    				transplant(y, y->right);
    			}
    			else if (is_nil(y->right)){
    				x = y->left;
    				transplant(y, y->left);
    			}
    			else {
    				node_pointer z = y;
    				y = tree_min(z->right);
    				y_original_is_black = y->is_black;
    				x = y->right;
    				if (y->parent != z){
    					transplant(y, y->right);
    					y->right = z->right;
    					z->right->parent = y;
    				}
    				transplant(z, y);
    				y->left = z->left;
    				y->left->parent = y;
    				y->is_black = z->is_black;
    			}
    			free_node(for_free);
    			if (y_original_is_black)
    				erase_fixup(x);
    			_size--;
    			_nil->parent = NULL;
    			if (_size == 0)
    				_root = _header;
    			else{
    				if (_size != 1)
    					x = tree_max(_root);
    				else
    					x = _root;
    				x->right = _header;
    				_header->parent = x;
    			}
    		}
            
            
            void erase_fixup(node_pointer x){
    			node_pointer brother;
    			while (x != _root && x->is_black){
    				if (x == x->parent->left){
    					brother = x->parent->right;
    					//case 1
    					if (!brother->is_black){
    						brother->is_black = true;
    						x->parent->is_black = false;
    						_rotate_left(x->parent);
    						brother = x->parent->right;
    					}
    					//case 2
    					if (brother->left->is_black && brother->right->is_black){
    						brother->is_black = false;
    						x = x->parent;
    					}
    					else{
    					//case 3
    						if (brother->right->is_black){
    							brother->left->is_black = true;
    							brother->is_black = false;
    							_rotate_right(brother);
    							brother = x->parent->right;
    						}
    					//case 4
    						brother->is_black = x->parent->is_black;
    						x->parent->is_black = true;
    						brother->right->is_black = true;
    						_rotate_left(x->parent);
    						x = _root;
    					}
    				}
    				else{
    					brother = x->parent->left;
    					//case 1
    					if (!brother->is_black){
    						brother->is_black = true;
    						x->parent->is_black = false;
    						_rotate_right(x->parent);
    						brother = x->parent->left;
    					}
    					//case 2
    					if (brother->right->is_black && brother->left->is_black){
    						brother->is_black = false;
    						x = x->parent;
    					}
    					else{
    					//case 3
    						if (brother->left->is_black){
    							brother->right->is_black = true;
    							brother->is_black = false;
    							_rotate_left(brother);
    							brother = x->parent->left;
    						}
    					//case 4
    						brother->is_black = x->parent->is_black;
    						x->parent->is_black = true;
    						brother->left->is_black = true;
    						_rotate_right(x->parent);
    						x = _root;
    					}
    				}
    			}
    		}
    

    6.RedBlackTreeのクリーンアップ

  • RedBlackTreeは、BinarchTreeの時間的複雑度o(logn)を決定するためのデータ構造であり、同じ役割を果たすAVLツリーとは異なり、メインメモリにデータを格納および処理するプログラミング言語におけるデータ構造に適している.