Data Structures and Algorithms - Binary Tree Exercises

Know the binary tree

  1. Recursive implementation of preorder, inorder and postorder
class Node<V>{
    
    
V value;
Node left;
Node right;
}

insert image description hereinsert image description hereAccording to the recursive order,
first order: print when it arrives for the first time, not do nothing for the first time
Inorder: print when it arrives at a node for the second time Postorder
: print when it arrives at a node for the third time

在这里插入代码片
  1. Non-recursive implementation

Any recursion can be changed to non-recursive
@@@Preorder traversal
Using the stack, the first step is to push the head node into the stack, and then play a fixed step (
1. Each time a node is popped up in the stack, it is recorded as current
2. Print current when it pops up
3. If there is one, push the child to the stack first right and then
left 4. Repeatedly)
insert image description here

Here is post-order traversal (two stacks need to be prepared, one original stack and one collection stack)

1 Pop up, record the current node as cur
2. Put cur into the collection stack
3. Push left first, then push right
4. Repeat again and again
insert image description here

Inorder traversal (mainly to find the left boundary)

For each subtree, the left boundary of the whole tree is pushed into the stack, during the process of popping up the nodes in turn, print, and then repeat the right tree of the popped node (the left boundary is pushed into the stack) own understanding: first put the left boundary of the whole
tree Push the stack, pop and print one by one, and push the left boundary of the right tree of the popped node into the stack (if any).
Why can this be done?
Because the left subtree can be decomposed (and can also be decomposed by the right subtree)
insert image description here

How to complete the breadth-first traversal of a binary tree (common question: Find the width of a binary tree)
The pre-order traversal of a binary tree is its depth-first traversal
insert image description here

The width of the tree is first traversed, using the queue (Queue q=new Linkedlist<>(), first enqueue the head node, pop up and print the node, and then enqueue the left and right children of the node (if any) ), the code is above

123

Method 1 (Using Hash Table). Prepare a table (HashMap). This table knows which layer he is in
. Table). Prepare 4 things, Node curEnd, the last node of the current layer; Node nextEnd, the last node of the next layer; int currentLevelNodes, the number of nodes in the current layer; int max, compared with currentLevelNodes, every time When reaching the last node of the current layer, grab max

Recursive Routine of Binary Tree

insert image description hereSearch binary tree: For any node, its left subtree is smaller than him, and its right subtree is larger than him.
(1) isBST, the first method - use inorder traversal to compare the size of the value (dynamic inspection is not very understandable, the second method - static inspection is easy to understand, use a List to store nodes, and then get the List to traverse, in ascending order , is to search the binary tree)

	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);
	}

It should be regarded as the third method - recursive routine solution

	//判断是否是搜索二叉树的第三种方法,(递归套路)
	//本来只需要四个条件,就可以判断一棵树是否是搜索二叉树,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, case a has right but no left is false, case b, in case a does not violate the rules, it is the first time that the left and right children are not both, and all the next must be leaf nodes
insert image description here

//当前代码是完全二叉树的判断
	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) Judging the full binary tree
Find the maximum depth L of the tree, the number of nodes in the tree N, if it satisfies 2 to the L power -1=N, it is true and
solve it with recursive routines

	//判断是否是满二叉树,需要的信息,树的高度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) Judging whether it is a balanced binary tree, the recursive routine of the binary tree
insert image description here

	//是否是平衡二叉树
	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);
		
	}

Routine can solve tree-shaped DP (you need to ask for information from the left tree, and also want to ask for information from the right tree to solve the information of the head) – prepare to search for tree-shaped DP topic Practice
topic: Given two binary tree nodes node1 and node2 , find their lowest common ancestor node
The first solution:

	//题目:给定两个二叉树的结点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;
		
	}

The second solution is too difficult to understand
insert image description here

Topic 4
insert image description hereSerialization and deserialization
Serialization: the tree in memory becomes a string on the hard disk
Deserialization: the string on the hard disk becomes a tree in memory
Serialization code

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

Serialization test code

  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);	 
	}

Serialize test results – into a string like this
insert image description here

deserialization code

   //反序列化-由字符串建树
	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;
}

Deserialization test code

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

Test result: the value of the returned head node is 1

insert image description here

Guess you like

Origin blog.csdn.net/qq_42373007/article/details/123697592