データ構造とアルゴリズム Six Trees

二分木の紹介

前に実装したシンボル テーブルでは、シンボル テーブルの追加、削除、およびクエリ操作を確認することは難しくありません。要素数 N が増加すると、時間も直線的に増加し、時間の複雑さは O (n). 作業効率を上げるために、次にツリーのデータ構造を学習します。

1.1 ツリーの基本定義

木は私たちのコンピューターにとって非常に重要なデータ構造であると同時に、家系図やユニットの組織構造など、実生活の多くのことを木のデータ構造を使用して記述することができます。

ツリーは、n (n>=1) 個の有限ノードで構成される一連の階層関係です。根が上を向き、葉が下を向いている逆さまの木のように見えることから「ツリー」と呼ばれます。

ここに画像の説明を挿入

木には次の特徴があります。

  1. 各ノードには 0 個以上の子ノードがあります。
  2. 親ノードのないノードはルート ノードです。
  3. 各非ルート ノードには、親ノードが 1 つだけあります。
  4. 各ノードとその子孫ノードは、全体としてツリーと見なすことができます。これは、現在のノードの親ノードのサブツリーと呼ばれます。

1.2 木に関する用語

ノードの次数:
ノードに含まれるサブツリーの数は、ノードの次数と呼ばれます。

リーフノード:
次数が 0 のノードはリーフノードと呼ばれ、ターミナルノードとも呼ばれます

分岐ノード:
次数が 0 でないノードは分岐ノードと呼ばれ、非終端ノードとも呼ばれます。

ノードのレベル:
ルート ノードから開始して、ルート ノードのレベルは 1、ルートの直接の後継レベルは 2 など

ノードの層順番号付け:
ツリー内のノードを、上層から下層へ、同じ層内で左から右へと直線的に並べ、連続する自然数にコンパイルします。

ツリーの次数:
ツリー内のすべてのノードの最大次数

ツリーの高さ (深さ):
ツリー内のノードの最大レベル

フォレスト:
m (m>=0) のばらばらなツリーの集合で、空でないツリーのルート ノードを削除すると、ツリーはフォレストになります; 統合されたルート ノードをフォレストに追加すると、フォレストはツリーの子になり
ここに画像の説明を挿入
ますノード:
ノードの直接の後続ノードは、ノードの子ノードと呼ばれます

親ノード (親ノード):
ノードの直接の先行ノードは、ノードの親ノードと呼ばれます。

兄弟ノード:
同じ親ノードの子ノードは兄弟ノードと呼ばれます

1.3 二分木の基本定義

二分木は、次数が 2 以下の木です (各ノードには最大 2 つのサブノードがあります).
ここに画像の説明を挿入
完全な二分木:
二分木. 各層のノード ツリーが最大値に達すると、二分木は完全な二分木 完全な二分木
ここに画像の説明を挿入
:
葉ノード 最下層と下から 2 番目の層にのみ出現する二分木で、最下層のノードはすべて層の左端に集中しています。
ここに画像の説明を挿入

1.4 二分探索木の作成

1.4.1 二分木のノードクラス

グラフの観察によると、二分木は実際には1つのノードとそれらの間の関係で構成されていることがわかりました.オブジェクト指向の考え方に従って、ノードを記述するノードクラスを設計します.

ノード クラス API の設計:
ここに画像の説明を挿入
コードの実装:

private class Node<Key,Value>{
    
    
	//存储键
	public Key key;
	//存储值
	private Value value;
	//记录左子结点
	public Node left;
	//记录右子结点
	public Node right;
	public Node(Key key, Value value, Node left, Node right) {
    
    
		this.key = key;
		this.value = value;
		this.left = left;
		this.right = right;
	}
}

1.4.2 二分探索木 API の設計

ここに画像の説明を挿入

1.4.3 二分探索木の実装

挿入メソッド put はアイデアを実装します:

  1. 現在のツリーにノードがない場合は、新しいノードを直接ルート ノードとして使用します。
  2. 現在のツリーが空でない場合は、ルート ノードから開始します:
    2.1 新しいノードのキーが現在のノードのキーより小さい場合は、現在のノードの左側の子ノードを探し続けます
    。新しいノードが現在のノードよりも大きい 2.3 新しいノードのキーが
    現在のノードのキーと等しい場合、そのようなノードがツリーに既に存在する場合は、ノードの値を置き換えるだけです。

ここに画像の説明を挿入
ここに画像の説明を挿入

クエリ メソッドは実装のアイデアを取得します。

ルート ノードから開始します。

  1. 照会するキーが現在のノードのキーより小さい場合は、現在のノードの左側の子ノードを検索し続けます。
  2. 照会するキーが現在のノードのキーより大きい場合は、現在のノードの正しい子ノードを探し続けます。
  3. 照会するキーが現在のノードのキーと等しい場合、現在のノードの値がツリーに返されます。

メソッドの削除の実装のアイデアを削除します。

  1. 削除されたノードを見つけます。
  2. 削除されたノードの右側のサブツリーで最小のノード minNode を見つけます
  3. 右側のサブツリーの最小ノードを削除します
  4. 削除されたノードの左部分木を最小ノード minNode の左部分木と呼び、削除されたノードの右部分木を最小ノード minNode の右部分木と呼ぶことにします。
  5. 削除されたノードの親ノードが最小ノード minNode を指すようにする
    ここに画像の説明を挿入
    ここに画像の説明を挿入
    ここに画像の説明を挿入

コード:

//二叉树代码
public class BinaryTree<Key extends Comparable<Key>, Value> {
    
    
	//记录根结点
	private Node root;
	//记录树中元素的个数
	private int N;
	//获取树中元素的个数
	public int size() {
    
    
		return N;
	}
	//向树中添加元素key-value
	public void put(Key key, Value value) {
    
    
		root = put(root, key, value);
	}
	//向指定的树x中添加key-value,并返回添加元素后新的树
	private Node put(Node x, Key key, Value value) {
    
    
		if (x == null) {
    
    
			//个数+1
			N++;
			return new Node(key, value, null, null);
		}
		int cmp = key.compareTo(x.key);
		if (cmp > 0) {
    
    
			//新结点的key大于当前结点的key,继续找当前结点的右子结点
			x.right = put(x.right, key, value);
		} else if (cmp < 0) {
    
    
			//新结点的key小于当前结点的key,继续找当前结点的左子结点
			x.left = put(x.left, key, value);
		} else {
    
    
			//新结点的key等于当前结点的key,把当前结点的value进行替换
			x.value = value;
		}
		return x;
	}
	//查询树中指定key对应的value
	public Value get(Key key) {
    
    
		return get(root, key);
	}
	//从指定的树x中,查找key对应的值
	public Value get(Node x, Key key) {
    
    
		if (x == null) {
    
    
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp > 0) {
    
    
			//如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;
			return get(x.right, key);
		} else if (cmp < 0) {
    
    
			//如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
			return get(x.left, key);
		} else {
    
    
			//如果要查询的key等于当前结点的key,则树中返回当前结点的value。
			return x.value;
		}
	}
	//删除树中key对应的value
	public void delete(Key key) {
    
    
		root = delete(root, key);
	}
	//删除指定树x中的key对应的value,并返回删除后的新树
	public Node delete(Node x, Key key) {
    
    
		if (x == null) {
    
    
			return null;
		}
		int cmp = key.compareTo(x.key);
		if (cmp > 0) {
    
    
			//新结点的key大于当前结点的key,继续找当前结点的右子结点
			x.right = delete(x.right, key);
		} else if (cmp < 0) {
    
    
			//新结点的key小于当前结点的key,继续找当前结点的左子结点
			x.left = delete(x.left, key);
		} else {
    
    
			//新结点的key等于当前结点的key,当前x就是要删除的结点
			//1.如果当前结点的右子树不存在,则直接返回当前结点的左子结点
			if (x.right == null) {
    
    
				return x.left;
			}
			//2.如果当前结点的左子树不存在,则直接返回当前结点的右子结点
			if (x.left == null) {
    
    
				return x.right;
			}
			//3.当前结点的左右子树都存在
			//3.1找到右子树中最小的结点
			Node minNode = x.right;
			while (minNode.left != null) {
    
    
				minNode = minNode.left;
			}
			//3.2删除右子树中最小的结点
			Node n = x.right;
			while (n.left != null) {
    
    
				if (n.left.left == null) {
    
    
					n.left = null;
				} else {
    
    
					n = n.left;
				}
			}
			//3.3让被删除结点的左子树称为最小结点minNode的左子树,让被删除结点的右子树称为最小结点
			minNode的右子树
			minNode.left = x.left;
			minNode.right = x.right;
			//3.4让被删除结点的父节点指向最小结点minNode
			x = minNode;
			//个数-1
			N--;
		}
		return x;
	}
	private class Node {
    
    
		//存储键
		public Key key;
		//存储值
		private Value value;
		//记录左子结点
		public Node left;
		//记录右子结点
		public Node right;
		public Node(Key key, Value value, Node left, Node right) {
    
    
			this.key = key;
			this.value = value;
			this.left = left;
			this.right = right;
		}
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<Integer, String> bt = new BinaryTree<>();
		bt.put(4, "二哈");
		bt.put(1, "张三");
		bt.put(3, "李四");
		bt.put(5, "王五");
		System.out.println(bt.size());
		bt.put(1,"老三");
		System.out.println(bt.get(1));
		System.out.println(bt.size());
		bt.delete(1);
		System.out.println(bt.size());
	}
}

1.4.4 その他の二分探索木の便利な方法

1.4.4.1 二分木で最小のキーを見つける

場合によっては、ツリー内のすべての要素を格納するキーの最小値を見つける必要があります. たとえば、ツリーには生徒のランキングと名前のデータが格納されているため、最も低いランキングは何かを調べる必要があります。ここでは、これを実現するために次の 2 つの方法を設計します。
ここに画像の説明を挿入

//找出整个树中最小的键
public Key min(){
    
    
	return min(root).key;
}
//找出指定树x中最小的键所在的结点
private Node min(Node x){
    
    
	if (x.left!=null){
    
    
		return min(x.left);
	}else{
    
    
		return x;
	}
}

1.4.4.2 二分木で最大のキーを見つける

場合によっては、ツリー内のすべての要素を格納するキーの最大値を見つける必要があります. たとえば、ツリーには生徒の成績と名前が格納されているため、見つける必要がある最高スコアはいくつでしょうか? ここでは、完了するための 2 つの方法も設計します。
ここに画像の説明を挿入

//找出整个树中最大的键
public Key max(){
    
    
	return max(root).key;
}
//找出指定树x中最大键所在的结点
public Node max(Node x){
    
    
	if (x.right!=null){
    
    
		return max(x.right);
	}else{
    
    
		return x;
	}
}

1.5 二分木の基本的な走査

多くの場合、ツリーに格納されている各要素を取り出すために、配列の配列をトラバースするようにツリーをトラバースする必要があります. ツリー構造は線形構造とは異なるため、最初から逆方向にトラバースすることはできません.トラバーサルとは、どのような検索パスをトラバースするかという問題です。
ここに画像の説明を挿入
上図のようにルートノード、左部分木、右部分木からなる木を単純に描くと、ルートノードにアクセスするタイミングによって、二分木の走査を次の3つに分けることができます。方法:

  1. トラバーサルを事前順序付けします。
    最初にルート ノードにアクセスし、次に左のサブツリーにアクセスし、最後に右のサブツリーにアクセスします。
  2. 順序通りのトラバーサル。
    最初に左側のサブツリーにアクセスし、中央のルート ノードにアクセスして、最後に右側のサブツリーにアクセスします。
  3. ポストオーダー トラバーサル。
    最初に左のサブツリーにアクセスし、次に右のサブツリーにアクセスし、最後にルート ノードにアクセスします。

以下のツリーをトラバースするために 3 つのトラバーサル メソッドを使用すると、結果は次のようになります。
ここに画像の説明を挿入

1.5.1 予約注文トラバーサル

4.4 で作成したツリーに、事前注文トラバーサル用の API を追加します。

public Queue<Key> preErgodic():使用前序遍历,获取整个树中的所有键
private void preErgodic(Node x,Queue<Key> keys):使用前序遍历,把指定树x中的所有键放入到keys队列中

実装プロセスでは、事前注文をたどり、各ノードのキーを取り出し、キューに入れ、返します。
実装手順:

  1. 現在のノードのキーをキューに入れます。
  2. 現在のノードの左側のサブツリーを検索します。空でない場合は、左側のサブツリーを再帰的にトラバースします
  3. 現在のノードの右側のサブツリーを検索します。空でない場合は、右側のサブツリーを再帰的にトラバースします

コード:

//使用前序遍历,获取整个树中的所有键
public Queue<Key> preErgodic(){
    
    
	Queue<Key> keys = new Queue<>();
	preErgodic(root,keys);
	return keys;
}

//使用前序遍历,把指定树x中的所有键放入到keys队列中
private void preErgodic(Node x,Queue<Key> keys){
    
    
	if (x==null){
    
    
		return;
	}
	//1.把当前结点的key放入到队列中;
	keys.enqueue(x.key);
	//2.找到当前结点的左子树,如果不为空,递归遍历左子树
	if (x.left!=null){
    
    
		preErgodic(x.left,keys);
	}
	//3.找到当前结点的右子树,如果不为空,递归遍历右子树
	if (x.right!=null){
    
    
		preErgodic(x.right,keys);
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<String, String> bt = new BinaryTree<>();
		bt.put("E", "5");
		bt.put("B", "2");
		bt.put("G", "7");
		bt.put("A", "1");
		bt.put("D", "4");
		bt.put("F", "6");
		bt.put("H", "8");
		bt.put("C", "3");
		Queue<String> queue = bt.preErgodic();
		for (String key : queue) {
    
    
			System.out.println(key+"="+bt.get(key));
		}
	}
}

1.5.2 インオーダー・トラバーサル

1.4 で作成したツリーに、事前注文トラバーサル用の API を追加します。

public Queue<Key> midErgodic():使用中序遍历,获取整个树中的所有键
private void midErgodic(Node x,Queue<Key> keys):使用中序遍历,把指定树x中的所有键放入到keys队列中

実装手順:

  1. 現在のノードの左側のサブツリーを検索します。空でない場合は、左側のサブツリーを再帰的にトラバースします
  2. 現在のノードのキーをキューに入れます。
  3. 現在のノードの右側のサブツリーを検索します。空でない場合は、右側のサブツリーを再帰的にトラバースします

コード:

//使用中序遍历,获取整个树中的所有键
public Queue<Key> midErgodic(){
    
    
	Queue<Key> keys = new Queue<>();
	midErgodic(root,keys);
	return keys;
}

//使用中序遍历,把指定树x中的所有键放入到keys队列中
private void midErgodic(Node x,Queue<Key> keys){
    
    
	if (x==null){
    
    
		return;
	}
	//1.找到当前结点的左子树,如果不为空,递归遍历左子树
	if (x.left!=null){
    
    
		midErgodic(x.left,keys);
	}
	//2.把当前结点的key放入到队列中;
	keys.enqueue(x.key);
	//3.找到当前结点的右子树,如果不为空,递归遍历右子树
	if (x.right!=null){
    
    
		midErgodic(x.right,keys);
	}
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<String, String> bt = new BinaryTree<>();
		bt.put("E", "5");
		bt.put("B", "2");
		bt.put("G", "7");
		bt.put("A", "1");
		bt.put("D", "4");
		bt.put("F", "6");
		bt.put("H", "8");
		bt.put("C", "3");
		Queue<String> queue = bt.midErgodic();
		for (String key : queue) {
    
    
			System.out.println(key+"="+bt.get(key));
		}
	}
}

1.5.3 ポストオーダートラバーサル

1.4 で作成したツリーに、事前注文トラバーサル用の API を追加します。

public Queue<Key> afterErgodic():使用后序遍历,获取整个树中的所有键
private void afterErgodic(Node x,Queue<Key> keys):使用后序遍历,把指定树x中的所有键放入到keys队列中

実装手順:

  1. 現在のノードの左側のサブツリーを検索します。空でない場合は、左側のサブツリーを再帰的にトラバースします
  2. 現在のノードの右側のサブツリーを検索します。空でない場合は、右側のサブツリーを再帰的にトラバースします
  3. 現在のノードのキーをキューに入れます。

コード

//使用后序遍历,获取整个树中的所有键
public Queue<Key> afterErgodic(){
    
    
	Queue<Key> keys = new Queue<>();
	afterErgodic(root,keys);
	return keys;
}
//使用后序遍历,把指定树x中的所有键放入到keys队列中
private void afterErgodic(Node x,Queue<Key> keys){
    
    
	if (x==null){
    
    
		return;
	}
	//1.找到当前结点的左子树,如果不为空,递归遍历左子树
	if (x.left!=null){
    
    
		afterErgodic(x.left,keys);
	}
	//2.找到当前结点的右子树,如果不为空,递归遍历右子树
	if (x.right!=null){
    
    
		afterErgodic(x.right,keys);
	}
	//3.把当前结点的key放入到队列中;
	keys.enqueue(x.key);
}
//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<String, String> bt = new BinaryTree<>();
		bt.put("E", "5");
		bt.put("B", "2");
		bt.put("G", "7");
		bt.put("A", "1");
		bt.put("D", "4");
		bt.put("F", "6");
		bt.put("H", "8");
		bt.put("C", "3");
		Queue<String> queue = bt.afterErgodic();
		for (String key : queue) {
    
    
			System.out.println(key+"="+bt.get(key));
		}
	}
}

1.6 二分木のレベル順トラバーサル

いわゆる層順トラバーサルは、ルート ノード (最初の層) から開始し、各層のすべてのノードの値を取得するために下に移動することです. 次のような二分木があります
ここに画像の説明を挿入
。 traversal は:
4.4 で作成した EBGADFHC ツリーで、層順トラバーサル用の API を追加します。

public Queue<Key> layerErgodic():使用层序遍历,获取整个树中的所有键

実装手順:

  1. 各レイヤーのノードを格納するキューを作成します。
  2. ループを使用してキューからノードをポップします:
    2.1 現在のノードのキーを取得します;
    2.2 現在のノードの左の子ノードが空でない場合、左の子ノードをキューに入れます
    2.3 の右の子ノードの場合現在のノード 子ノードが空でない場合は、正しい子ノードをキューに入れます

ここに画像の説明を挿入
ここに画像の説明を挿入
コード

//使用层序遍历得到树中所有的键
public Queue<Key> layerErgodic(){
    
    
	Queue<Key> keys = new Queue<>();
	Queue<Node> nodes = new Queue<>();
	nodes.enqueue(root);
	while(!nodes.isEmpty()){
    
    
		Node x = nodes.dequeue();
		keys.enqueue(x.key);
		if (x.left!=null){
    
    
			nodes.enqueue(x.left);
		}
		if (x.right!=null){
    
    
			nodes.enqueue(x.right);
		}
    }
	return keys;
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<String, String> bt = new BinaryTree<>();
		bt.put("E", "5");
		bt.put("B", "2");
		bt.put("G", "7");
		bt.put("A", "1");
		bt.put("D", "4");
		bt.put("F", "6");
		bt.put("H", "8");
		bt.put("C", "3");
		Queue<String> queue = bt.layerErgodic();
		for (String key : queue) {
    
    
			System.out.println(key+"="+bt.get(key));
		}
	}
}

1.7 二分木の最大深さ問題

要件:
与えられたツリーについて、ツリーの最大深度 (ツリーのルート ノードから最も遠いリーフ ノードまでの最長パス上のノードの数) を計算してください。
ここに画像の説明を挿入

上のツリーの深さは最大 4 です。

実装:
1.4 で作成されたツリーの最大深度を見つけるために、次の API を追加します:
public int maxDepth(): ツリー全体の最大深度を計算します
private int maxDepth(Node x): 指定されたツリー x の最大深度を計算します

実装手順:

  1. ルート ノードが空の場合、最大深度は 0 です。
  2. 左サブツリーの最大深さを計算します。
  3. 右部分木の最大深さを計算します。
  4. 現在のツリーの最大深度 = 左サブツリーの最大深度と右サブツリーの最大深度の大きい方 + 1

コード:

//计算整个树的最大深度
public int maxDepth() {
    
    
	return maxDepth(root);
}
//计算指定树x的最大深度
private int maxDepth(Node x) {
    
    
	//1.如果根结点为空,则最大深度为0;
	if (x == null) {
    
    
		return 0;
	}
	int max = 0;
	int maxL = 0;
	int maxR = 0;
	//2.计算左子树的最大深度;
	if (x.left != null) {
    
    
		maxL = maxDepth(x.left);
	}
	//3.计算右子树的最大深度;
	if (x.right != null) {
    
    
		maxR = maxDepth(x.right);
	}
	//4.当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1
	max = maxL > maxR ? maxL + 1 : maxR + 1;
	return max;
}

//测试代码
public class Test {
    
    
	public static void main(String[] args) throws Exception {
    
    
		BinaryTree<String, String> bt = new BinaryTree<>();
		bt.put("E", "5");
		bt.put("B", "2");
		bt.put("G", "7");
		bt.put("A", "1");
		bt.put("D", "4");
		bt.put("F", "6");
		bt.put("H", "8");
		bt.put("C", "3");
		int i = bt.maxDepth();
		System.out.println(i);
	}
}

1.8 折り紙の問題

必要なもの:
短冊をテーブルの上に垂直に置き、下から上に一度半分に折り、折り目を押し出して広げてください。このとき、折り目は凹んでいます。つまり、折り目の突起の方向が紙片の裏側を向いています。短冊を下から上に連続して2つ折りし、折り目を押し出して広げた場合、このとき、上から下の折り目、下の折り目、上の折り目の3つの折り目があります。下。

入力パラメータ N を指定すると、短冊を下から上に N 回連続して半分に折ることを意味します。すべての折り目の方向を上から下に印刷してください。 N=2 の場合、出力: 下 下 上

ここに画像の説明を挿入
分析:
中折り後に紙を裏返し、ピンク色を下に向けます.このとき、最初の半折りで発生した折り目をルートノードと見なし、2番目の半折りで発生した下の折り目をノードの左側. 子ノード, 2番目の半分折りによって生成された上部の折り目は、ノードの右側の子ノードであるため、ツリーデータ構造を使用して、半分折り後に生成された折り目を記述できます.

このツリーには次のような特徴があります。

  1. ルート ノードは下の折り目です。
  2. 各ノードの左の子ノードは下の折り目です。
  3. 各ノードの右側の子ノードは上の折り目です。

ここに画像の説明を挿入
実装手順:

  1. ノードクラスを定義する
  2. 深さ N の折り目ツリーを作成します。
  3. ツリー内のすべてのノードの内容を出力するには、順序通りのトラバーサルを使用します。

深さ N の折り目ツリーを作成します。

  1. 初めて半分に折り、折り目を 1 つだけ付け、ルート ノードを作成します。
  2. 最初のハーフ フォールドでない場合は、キューを使用してルート ノードを保存します。
  3. キューをループします:
    3.1 キューからノードを取り出します;
    3.2 このノードの左の子ノードが空でない場合、この左の子ノードをキューに追加します;
    3.3 このノードの右の子ノードが空でない場合、右の子ノードをキューに追加します;
    3.4 現在のノードの左の子ノードと右の子ノードが空でないかどうかを判断します。空である場合は、値を下にして現在のノードの値を作成する必要があります A 左の子値が up のノードと、値が up の右側の子ノード。

コード:

public class PaperFolding {
    
    
	public static void main(String[] args) {
    
    
		//构建折痕树
		Node tree = createTree(3);
		//遍历折痕树,并打印
		printTree(tree);
	}
	
	//3.使用中序遍历,打印出树中所有结点的内容;
	private static void printTree(Node tree) {
    
    
		if (tree==null){
    
    
			return;
		}
		printTree(tree.left);
		System.out.print(tree.item+",");
		printTree(tree.right);
	}
	
	//2.构建深度为N的折痕树;
	private static Node createTree(int N) {
    
    
		Node root = null;
		for (int i = 0; i <N ; i++) {
    
    
		if (i==0){
    
    
			//1.第一次对折,只有一条折痕,创建根结点;
			root = new Node("down",null,null);
		}else{
    
    
				//2.如果不是第一次对折,则使用队列保存根结点;
				Queue<Node> queue = new Queue<>();
				queue.enqueue(root);
				//3.循环遍历队列:
				while(!queue.isEmpty()){
    
    
					//3.1从队列中拿出一个结点;
					Node tmp = queue.dequeue();
					//3.2如果这个结点的左子结点不为空,则把这个左子结点添加到队列中;
					if (tmp.left!=null){
    
    
						queue.enqueue(tmp.left);
					}
					//3.3如果这个结点的右子结点不为空,则把这个右子结点添加到队列中;
					if (tmp.right!=null){
    
    
						queue.enqueue(tmp.right);
					}
					//3.4判断当前结点的左子结点和右子结点都不为空,如果是,则需要为当前结点创建一个值为down的左子结点,一个值为up的右子结点。
					if (tmp.left==null && tmp.right==null){
    
    
						tmp.left = new Node("down",null,null);
						tmp.right = new Node("up",null,null);
					}
				}
			}
		}
		return root;
	}
	//1.定义结点类
	private static class Node{
    
    
	//存储结点元素
	String item;
	//左子结点
	Node left;
	//右子结点
	Node right;

 //结点类
    private static class Node<T>{
    
    
        public T item;//存储元素
        public Node left;
        public Node right;

        public Node(T item, Node left, Node right) {
    
    
            this.item = item;
            this.left = left;
            this.right = right;
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/qq_33417321/article/details/121982459
Recomendado
Clasificación