データ構造とアルゴリズム - 二分探索ツリーと赤黒ツリー

1: 二分探索木

次の構造を見てみましょう。下の図の二分探索ツリーは、二分探索ツリーおよび二分ソート ツリーとも呼ばれます。

次のような特徴があります。

        1. 左のサブツリーが空でない場合、左のサブツリー上のノードの値はルート ノードより小さくなります。

        2. 右のサブツリーが空でない場合、右のサブツリー上のノードの値はルート ノードより大きくなります。

        3. サブツリーも上記 2 点に従う必要があります。

なぜバイナリソートツリーと呼ばれるのでしょうか?

        バイナリ ツリーのトラバーサル方法:前層、中間層、後層 (Mysql)

        ツリーが二分探索ツリーである限り、その順序内走査は順序通りでなければなりません

        左ルート (出力) 右側のバイナリ ツリーを見ると、その順序トラバーサルは次のようになります: 左ルート 右 左ルート (出力) 右: 0 3 4 5 6 8

         これは二分探索アルゴリズムに似ています。数値を推測すると、0 から 100 までの数値が表示され、推測できるようになります。推測結果が大きすぎるか小さすぎるかを毎回教えてくれます。50 は大きすぎます -> 51 ~ 100 は小さすぎます 0 ~ 50、それぞれの推測でスペースの半分を削除できます マージ ソート、ログオン、順序付けされたシーケンス

        2:二分探索木の追加、削除、変更、問い合わせ

        二分探索挿入 5 3 6 0 4 8

        挿入時には毎回ルートノードと比較されます。挿入すべき場所を常に見つけてください。必ずリーフノードに挿入されます。

        実際、挿入することが実際には検索であることがわかります。

        追加・変更チェック:簡易、主に削除

        削除は次の 3 つの状況に分類できます。 

                1. 削除対象のノードはリーフノード O(1)

                2. 削除するノードにはサブツリーが 1 つだけ (左または右) O(1) しかありません。

                3. 削除するノードには 2 つのサブツリーがあります。後続ノードを見つけます。後続ノードの左側のサブツリーは空でなければなりません。

        注: ここでの後続ノードは、削除される右側の最初のノードではなく、それより大きい最初のノードです。そうでない場合は、二分探索木の規則に準拠しないため、ソートできません。ノードのみが子ノードの場合は、削除の 2 番目のケースに相当します。そこで赤黒木が登場しました!

        パフォーマンス:

                ログの検索

                挿入: nlogn。挿入する数値は n 個あります。各数値はまずその位置を見つける必要があります。これが logn です。合わせて nlogn です。単一の数値を挿入する場合は、logn である必要があります。

       3: 二分木の応用

        二分探索木の応用は何ですか?

                O(n) for(int i = 0; i < n; i++){} 下の図と同様に、これも二分木ですが、順序付けされたシーケンスを挿入します。真ん中で分離された木が現れます。欠点はリンクリストに退化していることですが、検索ツリーなので検索にはそれを使用しなければなりません。その検索時間の計算量を分析してみましょう: 右側の 2 つの二分探索ツリーを見てください: それらのパフォーマンス: 検索時間の計算量は実際にはツリーの深さ O(n) であり、これは時間計算量を意味します: N 回検索します。なぜ N 回循環 (縮退) されたのでしょうか? どうやって解決すればいいでしょうか?鎖のようにならないように

        AVL ツリー (絶対平衡ツリー): 赤黒ツリーと平衡二分木: AVL は実験室状態に属し、プロジェクトでは赤黒ツリーが使用されます。        

        3.1 バイナリコード

package tree;

public class BinarySeachTree {
	
	private int color = 0;		//0表示黑,1表示红
	int data;
	BinarySeachTree left;
	BinarySeachTree right;
	
	BinarySeachTree parent;
	
	public BinarySeachTree(int data) {
		this.data = data;
		this.left = null;
		this.parent = null;
		this.color = 1;
		this.right = null;
		//parent.parent	;爷爷
		//parent.parent.left 左边的叔叔
		//parent.left 兄弟姐妹
	}
	//插入的时候每次都是和根结点比较。一直要找到它应该插入的位置。
	//肯定会插在叶子结点。那么其实大家可以看到 插入其实就是查找。 默认root不会为空

	public void insert(BinarySeachTree root,int data) {
		//if(root == null) {}
		if(root.data < data) {	//根节点小 我们要放到右边
			if(root.right == null) {
				root.right = new BinarySeachTree(data);
			}else {
				insert(root.right, data);
			}
		}else {
			if(root.left == null) {
				root.left = new BinarySeachTree(data);
			}else {
				insert(root.left, data);
			}
		}
	}
	
	public void find(BinarySeachTree root,int data) {
		if(root != null) {
			if(root.data < data) {
				find(root.right, data);
			}else if(root.data > data) {
				find(root.left, data);
			}else {
				System.out.println("找到了");
				System.out.println(root.data);
				return ;
			}
		}
	}
	public void pre() {	
		
	}
	public void post() {	
		
	}
	public void in(BinarySeachTree root) {		//中序遍历
		if(root != null) {
			in(root.left);
			System.out.print(root.data + " ");
			in(root.right);
		}
	}
	
	public static void main(String[] args) {
		//快速排序,归并排序,二叉树排序
		int data[] = {0,5,9,1,2,3,10};
		BinarySeachTree root = new BinarySeachTree(data[0]);	//第一个点作为跟结点
		for(int i = 1 ; i < data.length ; i ++) {
			root.insert(root, data[i]);
		}
		System.out.println("中序遍历:");
		root.in(root);
	}
}

        4: 赤黒木の紹介 (所有物、左巻き、右巻き)

        上の 2 つの図から、バイナリ ツリーの構造が検索パフォーマンスを決定することがわかります。では、それをどのように最適化すればよいでしょうか?

        AVL の木と赤黒の木があります

        AVL ツリー: バランスの取れたバイナリ ツリー。左右のサブツリー間の高さの差は 1 を超えません。これにより確かに直線構造を回避できますが、最も理想的なものではありません。

        それは理想的な状態、実験室と考えることができます。赤黒い木

        なぜ?総合的なパフォーマンスの考慮事項に基づいて選択します。

        赤黒木の性質(重要なポイント):

                1. 各ノードは赤または黒のいずれかです

                2. 赤いノードを接続することはできません (黒いノードは問題ありません)。各リーフ ノードは黒い空のノード (NIL) です。つまり、リーフ ノードにはデータが格納されません。

                3. ルート ノードはすべて黒のルートです 4. 各ノード、およびそのノードから到達可能な葉ノードまでのすべてのパスには、同じ数の黒のノードが含まれます。

        4.2 赤黒木の 3 つの変形

                1. 色の変更: 最も単純、赤から黒、黒から赤

                2. 左回転: ポイントの回転の場合、ポイントの上のサブツリーも従う必要があります。ポインタ

                3. 右回転:

        では、上記の 3 つの方法をどのように選択すればよいのでしょうか?

       4.3 ルールの挿入

挿入時の回転と色変換のルール:

        1. 色の変化の状況: 現在のノードの親ノードは赤で、その祖父ノードの別の子ノードも赤です。(叔父ノード): (1) 親ノードを黒に設定します (2) 叔父を黒に設定します (3) 父の父親である祖父を赤に設定します (おじいちゃん) (4) 祖父ノードへのポインタを定義します (おじいちゃん) ) が現在の動作に設定されます。

        2. 左回転: 現在の親ノードが赤、叔父が黒の場合、現在のノードは右のサブツリーになります。左回転では、親ノードを左回転として使用します。ポインタが親ノードに変わります

        3. 右回転: 現在の親ノードが赤、叔父が黒の場合、現在のノードは左側のサブツリーになります。右回転 (1) 親ノードを黒にする (2) 祖父ノードを赤にする(おじいちゃん) (3) 祖父ノードを回転する(おじいちゃん)

赤黒ツリーの削除 正直に言うと、赤黒ツリーの削除が一番難しいので、ここではマスターしなければならない要件は書きませんが、二分探索の削除原理はマスターしておく必要があります。だって、左右のどんでん返しを徹底的に覚えたとしても、数日もすれば忘れてしまうこと請け合いです。赤黒ツリーのコード実装を学習しても、日々のプロジェクト開発にはあまり役に立ちません。ほとんどの人にとって、赤黒の木を今生で手書きすることは決してないかもしれません。また、アルゴリズム面接ではほとんど役に立ちません。通常であれば、赤黒木を手書きで書けとは普通の人は言わないでしょう。せいぜい原理について聞かれるだけですが、二分探索木は必ず書かなければならないものです。面接中に疑似コードを書くように求められる場合があります。

赤黒ツリーのパフォーマンス解析 挿入近似: nlogn logn の検索 削除: 近似 logn

赤黒ツリーのアプリケーション: 1. HashMap 2. TreeMap 3. Windows 最下層: 検索 4. Linux プロセス スケジューリング、nginx など。

おすすめ

転載: blog.csdn.net/qq_67801847/article/details/132788929