データ構造とアルゴリズム - 二分木の演習

二分木を知る

  1. preorder、inorder、postorder の再帰的な実装
class Node<V>{
    
    
V value;
Node left;
Node right;
}

ここに画像の説明を挿入ここに画像の説明を挿入再帰的な順序によると、
first order: 最初に到着したときに出力し、最初は何もしない
inorder: 2 回目のノードに到着したときに出力する postorder
: 3 回目のノードに到着したときに出力する時間

在这里插入代码片
  1. 非再帰的な実装

任意の再帰を非再帰
@@@Preorder トラバーサルに変更できます
。スタックを使用して、最初のステップはヘッド ノードをスタックにプッシュし、次に固定ステップを再生することです (
ノードがスタックにポップアップするたびに 1. 、それ
currentとして記録されます


ここに画像の説明を挿入

これがポストオーダーのトラバーサルです (2 つのスタックを用意する必要があります。1 つのオリジナル スタックと 1 つのコレクション スタックです)。

1 ポップアップし、現在のノードを cur として記録する
2. cur をコレクション スタックに入れる
3. 最初に左に押し、次に右に押す
4. 何度も繰り返す
ここに画像の説明を挿入

順序通りのトラバーサル (主に左側の境界を見つけるため)

サブツリーごとに、ツリー全体の左側の境界がスタックにプッシュされ、ノードを順番にポップアップし、印刷し、ポップされたノードの右側のツリーを繰り返します (左側の境界がスタックにプッシュされます)。自分の理解: 最初にツリー全体の左境界を入れて
スタックをプッシュし、1 つずつポップして出力し、ポップされたノードの右ツリーの左境界を (もしあれば) スタックにプッシュします

左のサブツリーは分解できるため (右のサブツリーでも分解できる)
ここに画像の説明を挿入

バイナリ ツリーの幅優先トラバーサルを完了する方法(よくある質問: バイナリ ツリーの幅を見つける
)
ここに画像の説明を挿入

ツリーの幅は最初にキュー (Queue q=new Linkedlist<>()) を使用してトラバースされ、最初にヘッド ノードをキューに入れ、ノードをポップアップして出力し、次にノードの左右の子をキューに入れます (存在する場合)。 ) )、コードは上記

123


方法 1 (ハッシュ テーブルを使用). テーブル (HashMap)を準備します.このテーブルは彼がどの層にいるかを知っています.次のレイヤー; int currentLevelNodes、現在のレイヤーのノード数; int max、currentLevelNodes と比較して、毎回、現在のレイヤーの最後のノードに到達したら、max を取得します

二分木の再帰ルーチン

ここに画像の説明を挿入二分木を検索: 任意のノードについて、その左の部分木は彼よりも小さく、その右の部分木は彼よりも大きい.
(1) isBST、最初の方法 - 順序トラバーサルを使用して値のサイズを比較します (動的検査はあまり理解できません。2 番目の方法 - 静的検査は理解しやすいです。リストを使用してノードを格納し、リストを取得します。昇順でトラバースすることは、二分木を検索することです)

	public int preValue=Integer.MIN_VALUE;//遍历过程中上一次出现的
	//用中序遍历动态检查是否是搜索二叉树(动态检查)---不理解
	public boolean isBST(Node head) {
    
    
		if(head==null) {
    
    
			return true;
		}
		boolean isLeftBST=isBST(head.left);//左树是不是搜索二叉树
		if(!isLeftBST) {
    
    
			return false;
		}
		//当前节点是否比我上一次处理的结点大
		//如果左树是搜索二叉树,head与左树最后一个打印的比较
		if(head.value<=preValue) {
    
    
			return false;
		}else {
    
    
			preValue=head.value;
		}
		
		System.out.print(head.value+" ");
		return isBST(head.right);//如果右树是搜索二叉树,整棵树就是了
		 
	}
	//用中序遍历检查是否是搜索二叉树(第二种方法静态检查)
	public boolean isBST2(Node head) {
    
    
		List<Node> inOrderList =new ArrayList<>();
		//得到按照中序排列的list
		process2(head,inOrderList);
			for(int i=0;i<inOrderList.size()-1;i++) {
    
    
				if(inOrderList.get(i).value>inOrderList.get(i+1).value) {
    
    
					return false;
				}
			}
		
		return true;
		
	}
	public void process2(Node head,List<Node> inOrderList) {
    
    //这个方法是把一个个结点按照中序遍历的顺序放到List中
		if(head==null) {
    
    
			return ;
		}
		process2(head.left,inOrderList);
		inOrderList.add(head);
		process2(head.right,inOrderList);
	}

3番目の方法と見なす必要があります-再帰ルーチンソリューション

	//判断是否是搜索二叉树的第三种方法,(递归套路)
	//本来只需要四个条件,就可以判断一棵树是否是搜索二叉树,1左树是否是搜索二叉树2左树最大值(要小于head)3右树是否是搜索二叉树4右树最小值(要大于head值)
	//使用递归,要求左树右树返回值一样,所以要三个变量,是否是搜索二叉树,最大值,最小值
	public class ReturnData {
    
    
		public boolean isBST;
		public int min;
		public int max;
		public ReturnData(boolean is,int mi,int ma) {
    
    
			isBST=is;
			min=mi;
			max=ma;
		}
		
	}
	public ReturnData process2(Node x) {
    
    
		if(x==null) {
    
    
		return null;
		}
		ReturnData leftData=process2(x.left);//左树给我三个信息
		ReturnData rightData=process2(x.right);//右树给我三个信息
		
		//我自己也要给出三个信息,递归才能连起来1.boolean isBST;2.int min;3.int max;
		
		int min=x.value;
		int max=x.value;
		if(leftData!=null) {
    
    //如果左树不为空,返回值就不是空的,可以返回信息,取左树的最小值和最大值
			min=Math.min(min, leftData.min);
			max=Math.max(max, leftData.max);
		}
		if(rightData!=null) {
    
    //如果右树不为空,返回值就不是空的,可以返回信息,取左树的最小值和最大值
			min=Math.min(min, rightData.min);
			max=Math.max(max, rightData.max);
		}
		boolean isBST=true;//先认为没有违规,返回true
		if(leftData!=null&&(!leftData.isBST||leftData.max>=x.value)) {
    
    
			//如果左树 有信息 并且左树已经不是搜索二叉树了违规;或者左树有信息 并且左树最大值大于等于x.value,就违规
			isBST=false;
		}
		if(rightData!=null&&(!rightData.isBST||rightData.min<=x.value)) {
    
    
			//如果右树 有信息 并且右树已经不是搜索二叉树了违规;或者右树有信息 并且左树最小值小于等于x.value,就违规
			isBST=false;
		}
		
		/**或者使用三目运算,
		 * boolean isBST=false;//先认为是false,寻找成立条件
		 * if(
		 * (leftData!=null?(leftData.isBST&&leftData.max<x.value):true)
		 * &&
		 * (rightData!=null?(rightData.isBST&&rightData.min>x.value):true)
		 * ){
		 * isBST=true;
		 * }
		 * */
		return new ReturnData(isBST,min,max);
		
	} 

(2) isCBT、a に右があるが左がない場合は false、b の場合、a が規則に違反していない場合、左と右の子が両方でないのは初めてで、次はすべて葉ノードでなければなりません
ここに画像の説明を挿入

//当前代码是完全二叉树的判断
	public boolean isCBT(Node head) {
    
    //采用宽度优先遍历
		if(head==null) {
    
    
			return false;
		}
		LinkedList<Node> queue=new LinkedList<>();
		boolean leaf=false;//是否遇到过左右孩子不双全的节点,一旦遇到,就一直是true了,因为将诶下来都是叶子结点
		Node l=null;
		Node r=null;
		queue.add(head);
		while(!queue.isEmpty()) {
    
    
			head=queue.poll();
			l=head.left;
			r=head.right;
			
			if(
				(l==null&&r!=null)//第一种,有右无左
				||//如果遇到了左右孩子不双全的结点之后,又发现当前结点居然有孩子,也可以这样写(leaf&&!(l==null||r==null))
				(leaf&&(l!=null||r!=null))//遇到了左右孩子不双全的结点(这种结点至多只能有一个),该结点它还有孩子,则返回false
					) {
    
    
				return false;
			}
			
			
			if(l!=null) {
    
    //l!=null,r!=null,l==null||r==null相当于宽度优先遍历
				queue.add(l);
			}
			if(r!=null) {
    
    
				queue.add(r);
			}
			if(l==null||r==null) {
    
    
				leaf=true;
			}
		}
		return true;
		
	}

(3) 完全な二分木の判定 木
の最大深さ L、木のノード数 N を求め、2 の L 乗 -1=N を満たす場合、真であり、再帰ルーチンで解きます

	//判断是否是满二叉树,需要的信息,树的高度h和结点个数n,
	public class Info{
    
    
		public int height;
		public int nodes;
		
		public Info(int h,int n) {
    
    
			height=h;
			nodes=n;
		}
	}
	public boolean FullBT(Node head) {
    
    
		if(head==null) {
    
    
			return true;
		}
		Info data=process3(head);//收整棵树的两个信息
		//判断,n是否等于2的L次方-1
		boolean res=(data.nodes==1<<data.height-1)?true:false;
		return res;
		
	}
	public Info process3(Node x) {
    
    
		
		if(x==null) {
    
    
			return new Info(0,0);
		}
		Info leftInfo=process3(x.left);//先向左树要信息
		Info rightInfo=process3(x.right);//再向右树要
		
		//加工自己的信息,树的高度及结点个数
		int height=Math.max(leftInfo.height, rightInfo.height)+1;
		int nodes=leftInfo.nodes+leftInfo.nodes;

		return new Info(height,nodes);
		
	}

(4) バランスのとれた二分木かどうかの判定、二分木の再帰ルーチン
ここに画像の説明を挿入

	//是否是平衡二叉树
	public boolean isBalanced(Node head) {
    
    
		return process(head).isBalanced;
		
	}
	public class ReturnType{
    
    
		public boolean isBalanced;
		public int height;
		public ReturnType(boolean isB,int hei){
    
    
			isBalanced=isB;
			height=hei;
		}
	}
	public ReturnType process(Node x) {
    
    
		if(x==null) {
    
    
			return new ReturnType(true,0);
		}
		ReturnType leftData=process(x.left);
		ReturnType rightData=process(x.right);
		int height=Math.max(leftData.height, rightData.height)+1;
		boolean isBalanced=leftData.isBalanced&&rightData.isBalanced
				&&Math.abs(leftData.height- rightData.height)<2;
		return new ReturnType(isBalanced,height);
		
	}

ルーチンは木型 DP を解くことができる (左の木から情報を求める必要があり、頭の情報を解くために右の木からも情報を求めたい) – 木型 DP トピックを検索する準備をする練習
トピック: 2 つのバイナリ ツリー ノード node1 と node2 が与えられた場合、それらの最も低い共通の祖先ノードを見つけます

	//题目:给定两个二叉树的结点node1和node2,找到他们的最低公共祖先结点
	public Node LowCommonAncestor(Node head,Node o1,Node o2) {
    
    
		HashMap<Node,Node> fatherMap=new HashMap<>();
		fatherMap.put(head, head);//大头结点的父是他自己
		process4(head,fatherMap);//这样一来所有结点的父就都找到了
		HashSet<Node> setO1=new HashSet<>(); //记录o1往上的链
		
		Node cur=o1;
		while(cur!=fatherMap.get(cur)) {
    
    //只有头结点的父节点才是他自己,当跳到head,结束循环
			setO1.add(cur);//把o1加入set
			cur=fatherMap.get(cur);//o1往上窜
			
		}
		setO1.add(head);//最后把head加入set
		
		Node cur2=o2;//让 o2一直网上窜去setO1里寻找o1,找到就返回
		if(!setO1.contains(cur2)) {
    
    
			cur2=fatherMap.get(cur2);
		}
		return cur2;
		
	}

2番目の解決策は難しすぎて理解できません
ここに画像の説明を挿入

トピック 4
ここに画像の説明を挿入シリアライゼーションとデシリアライゼーション
シリアライゼーション: メモリ内のツリーがハードディスク上の文字列になる
デシリアライゼーション: ハードディスク上の文字列がメモリ内のツリーになる
シリアライゼーション コード

	//以head为头的树,使用先序遍历,序列化为字符串返回
	public String serialByPre(Node head) {
    
    
		if(head==null) {
    
    
			return "#_";
		}
		String res=head.value+"_";
		res+=serialByPre(head.left);
		res+=serialByPre(head.right);
		return res;
		
	}

シリアル化テスト コード

  public static void main(String[] args) {
    
    
    	 XuLieHua xl=new XuLieHua();
    	 Node node1=xl.new Node(1);
    	 Node node2=xl.new Node(1);
    	 Node node3=xl.new Node(1);
    	 node1.right=node2;
    	 node2.left=node3;
    	 String xuliehuaRES=xl.serialByPre(node1);
    	 System.out.println(xuliehuaRES);	 
	}

テスト結果をシリアル化 – このような文字列に
ここに画像の説明を挿入

逆シリアル化コード

   //反序列化-由字符串建树
	public Node reconByPreString(String preStr) {
    
    
		String[] values=preStr.split("_");
		Queue<String> queue=new LinkedList<>();
		for(int i=0;i<values.length;i++) {
    
    
			queue.add(values[i]);
		}
		
		return reconPreOrder(queue);
		
	}
     public Node reconPreOrder(Queue<String> queue) {
    
    //不断消费一个队列
	  String value=queue.poll();
	  if(value.equals("#")) {
    
    
		  return null;
	  }
	  Node head=new Node(Integer.valueOf(value));//先建头
	  head.left=reconPreOrder(queue);//再建左子树
	  head.right=reconPreOrder(queue);//最后建右子树
	  return head;
}

逆シリアル化テスト コード

  public static void main(String[] args) {
    
    
    	 XuLieHua xl=new XuLieHua();
    	 Node head=xl.reconByPreString("1_#_1_1_#_#_#_");
    	 System.out.println(head.value);
	}

テスト結果: 返されたヘッド ノードの値は 1 です

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_42373007/article/details/123697592