数据结构与算法-二叉树练习

认识二叉树

  1. 递归实现先序中序后序
class Node<V>{
    
    
V value;
Node left;
Node right;
}

在这里插入图片描述在这里插入图片描述根据递归序得出,
先序:第一次到达时就打印,不是第一次什么也不做
中序:第二次来到节点时才打印
后序:第三次来到节点时才打印

在这里插入代码片
  1. 非递归实现

任何递归都可以改成非递归
@@@先序遍历
利用栈,第一步吧头结点压入栈中,然后玩固定的步骤(
1.每次在栈中弹出一个结点,记为current
2.弹出就打印打印current
3.如果有的话,把孩子先右后左压入栈中
4.周而复始)
在这里插入图片描述

这里是后序遍历(需要准备两个栈,一个原始栈,一个收集栈)

1 弹出,把当前节点记为cur
2.把cur放入收集栈
3.先压左在压右
4.周而复始
在这里插入图片描述

中序遍历(主要是找左边界)

每颗子树的,整棵树左边界进栈,依次弹出结点的过程中,打印,然后对弹出节点的右树重复(左边界进栈)
自己的理解:先将整棵树的左边界入栈,一个个弹出并打印,并将弹出节点右树的左边界入栈(如果有的话)
为什么可以这样做?
因为左子树可以被分解掉(也可以被右子树分解掉)
在这里插入图片描述

如何完成二叉树的宽度优先遍历(常见题目:求一颗二叉树的宽度)
二叉树的先序遍历就是它的深度优先遍历
在这里插入图片描述

树的宽度优先遍历,利用队列(Queue q=new Linkedlist<>(),先将头结点入队,弹出并打印该结点,再将该结点的左右孩子入队(如果有的话)),代码在上面

123

方法1(利用哈希表).准备一张表(HashMap),这张表知道他在第几层,(左神讲的有点繁琐,其实是我自己没听懂)
方法2(不利用哈希表).准备4个东西, Node curEnd,当前层的最后一个结点; Node nextEnd,下一层的最后一个结点;int currentLevelNodes,当前层的节点数;int max,与currentLevelNodes相比,每次 到达当前层最后一个结点时,抓取max

二叉树的递归套路

在这里插入图片描述搜索二叉树:对于任意一个结点,它的左子树都比他小,右子树都比他大。
(1)isBST,第一种-用中序遍历比较值的大小(动态检查的不太理解,第二种方法–静态检查好理解,用一个List存储结点,然后拿到List遍历,是升序 ,则是搜索二叉树)

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

应该算是第三种方法吧—递归套路解决

	//判断是否是搜索二叉树的第三种方法,(递归套路)
	//本来只需要四个条件,就可以判断一棵树是否是搜索二叉树,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,则是true
用递归套路求解

	//判断是否是满二叉树,需要的信息,树的高度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题目练习
题目:给定两个二叉树的结点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;
		
	}

第二种解法,太难了,不理解
在这里插入图片描述

题目四
在这里插入图片描述序列化与反序列化
序列化:内存中的树变为硬盘上的字符串
反序列化:硬盘上的字符串变为内存中的树
序列化代码

	//以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