树的一些简单算法

  1. 检查一颗树是否是搜索二叉树 当一个节点比它的左子树所有值大,比它的右子树的所有节点值小那么它就是搜索二叉树。
    用递归算法即可 我们根据左中右的顺序 可以知道最好使用中序遍历,先看左数是不是,如果不是直接返回false,此时我们需要比较左树的值和当前值比较如果比当前值小那么符合。同时赋值给preValue,给右子树使用。 我们细化到左子树里面其实我们知道,最后一个给preValue赋值的是最右边的的节点,这个节点一定是整棵树最大的值,细化到右边也同理。
//检查一棵树是否是搜索二叉树  任意一个节点肯定比他的左子树大 比它的右子树小 正好符合中序遍历的顺序
	public static int preValue=Integer.MIN_VALUE;
	public static boolean checkBST(Node<Integer> head){
		if(head==null)
			return true;
		if(!checkBST(head.left))//如果左节点的树不是 返回false
			return false;
		if(head.val<=preValue){//如果是 但是它的值比我们当前节点的值要大那么不是搜索二叉树  preValue此时记录的是当前树左子节点的值  
			return false;
		}
		preValue=head.val;//preValue修改为当前节点的值给它的右子节点使用
		return checkBST(head.right);
	}

不适用递归实现其实和使用基本差不多

//不用递归实现
	public static boolean mediumOrderDifferent(Node<Integer> node){
		Stack<Node> stack=new Stack<>();
		int preValue=Integer.MIN_VALUE;
		Node<Integer> temp=node;
		do{
			while(temp!=null){
				stack.push(temp);
				temp=temp.left;
			}
			temp=stack.pop();
			if(temp.val<=preValue){
				return false;
			}
			preValue=temp.val;
			temp=temp.right;
		}while(!stack.isEmpty()||temp!=null);//防止由于栈空 但是树并没有遍历结束而提前结束
		return true;
	}
//另外的实现
public static BSTResult getBST(Node<Integer> head){
		if(head==null)
			return null;
		BSTResult left=getBST(head.left);
		BSTResult right=getBST(head.right);
		int max=head.val;
		int min=head.val;
		if(left!=null){//给max和min赋值  求出该子树的最大和最小值
			max=Math.max(left.max, max);
			min=Math.min(left.min, min);
		}
		if(right!=null){
			max=Math.max(right.max, max);
			min=Math.min(right.min, min);
		}
		boolean isBST=true;
		if(left!=null&&(!left.isBST || left.max>=head.val))//如果左子树存在的情况下  左子树不是搜索二叉树或者 他的最大值大于等于当前节点的值  那这颗树就不是搜索二叉树
			isBST=false;
		if(right!=null&&(!right.isBST || right.min<=head.val))//如果右子树存在的情况下  右子树不是搜索二叉树或者 他的最小值小于等于当前节点的值 那这颗树就不是搜索二叉树
			isBST=false;
		return new BSTResult(max,min,isBST);
	}
  1. 如何判断一棵树是否是完全二叉树,宽度优先遍历遍历每一行的节点,必须满足一下两种情况:
    1.该节点不能够右节点不为空,左节点为空
    2.如果某个节点没有出现左右孩子都在的情况,那么它后面的节点一定是叶子节点(即左右孩子都没有)
//完全二叉树
	public static boolean isCBT(Node node){//使用队列即可完成宽度优先遍历
		Queue<Node> queue=new LinkedList<>();
		boolean flag=false;//判断该节点是否左右孩子双全 如果不双全 那么它的后面的节点必须都是叶节点 不能有子节点
		queue.add(node);
		while(!queue.isEmpty()){
			Node temp=queue.poll();
			if(flag&&(temp.left!=null||temp.right!=null)){
				return false;
			}
			if(temp.right!=null&&temp.right==null)
				return false;
			else if(!(temp.left!=null&&temp.right!=null))//当它只有左孩子没有右孩子 或者左右都没有孩子的话 它后面的节点都不能有孩子了
				flag=true;
			if(temp.left!=null)
				queue.add(temp.left);
			if(temp.right!=null)
				queue.add(temp.right);
		}
		return true;
	}
  1. 判断一颗二叉树是否是平衡二叉树 如果一棵树的左子树为平衡二叉树 右子树为平衡二叉树 同时左右子树的高度相差不能超过1

一看到这个描述就知道应该使用递归 ,但是它需要两个返回值类型,所以我们必须构造一个返回值类型的对象,里面装了高度和是否为平衡二叉树,只需要按如下顺序 先处理左子树和右子树,如果左子树是右子树是而且高度相差不大于1,那么他就是平衡二叉树。

public static ResultType isBanlance(Node<Integer> head){
		if(head==null)
			return new ResultType(true,0);
		
		ResultType left=isBanlance(head.left);
		ResultType right=isBanlance(head.right);
		
		int height=Math.max(left.height, right.height)+1;
		if(Math.abs(left.height-right.height)<2&&left.isBanlance&&left.isBanlance)
			return new ResultType(true,height);
		return new ResultType(false,height);
		
	}
	public static class ResultType{//返回值类型 需要高度和是否平衡
		boolean isBanlance;
		int height;
		public ResultType(boolean isBanlance,int height){
			this.isBanlance=isBanlance;
			this.height=height;
		}
		@Override
		public String toString() {
			return "ResultType [isBanlance=" + isBanlance + ", height="
					+ height + "]";
		}
		
	}
  1. 判断一棵树是否是满二叉树,一颗满二叉树的条件是节点数和它的高度关系为 nodes=2^height-1
    所以我们需要一个对象来装载高度和节点总数,剩下的就是简单的递归操作:
public static class Info{
		private int height;
		private int nodes;
		public Info(int height, int nodes) {
			super();
			this.height = height;
			this.nodes = nodes;
		}
		@Override
		public String toString() {
			return "Info [height=" + height + ", nodes=" + nodes + "]";
		}
	}

//判断是否是满二叉树
	public static Info isF(Node<Integer> head){
		if(head==null)
			return new Info(0,0);
		Info left=isF(head.left);
		Info right=isF(head.right);
		int height = Math.max(left.height, right.height)+1;
		int nodes = left.nodes+right.nodes+1;
		return new Info(height,nodes);
	}
  1. 求两个节点的公共祖先节点 首先第一种想法:先用一个HashMap装入每个节点和它的父节点的关系,然后从需要的节点node1开始向祖先节点一个一个的走,并且放入HashSet中,然后再从node2开始向它的祖先节点走如果遇到一个节点和node1节点的祖先节点相同那么这个节点就是我们需要的节点,代码如下:
//求两个节点的公共祖先节点
	public static Node theSameParentNode(Node head,Node node1,Node node2){
		//首先求出节点和它的父节点的关系 用map装起来
		HashMap<Node,Node> parentMap=new HashMap<>();
		process(head,parentMap);
		parentMap.put(head, head);//设置头节点的父节点就是它自己
		//将node1向上面回溯得到他的父节点树 装起来
		HashSet<Node> nodeSet=new HashSet<>();
		Node cur=node1;
		while(cur!=head){//当前节点不是父节点的时候
			nodeSet.add(cur);
			cur=parentMap.get(cur);
		}
		cur=node2;
		while(cur!=head){
			if(nodeSet.contains(cur))
				return cur;
			cur=cur=parentMap.get(cur);
		}
		return head;
	}
	public static void process(Node head,HashMap<Node,Node> parentMap){
		if(head==null)
			return;
		parentMap.put(head.left, head);
		parentMap.put(head.right, head);
		process(head.left,parentMap);
		process(head.right,parentMap);
	}

还有另一种算法,比较难以理解的:这个节点主要做了什么,就是如果递归到的节点是null返回null,是node1返回node1,是node2返回node2,如果都不是去他的左右节点找,如果左右节点都不为空那么证明我们找到的左右节点一定一个是node1,一个是node2,所以他们相遇的点就是当前节点,返回head。如果不是都不为空,那么就返回不为空的那个,都为空就返回空。
大家想 叶子节点左右都为空 那么返回值一定是空,如果它的父节点没有我们需要的node2或者node1节点那么它的返回值也一定是空。
另一种理解:整个过程只有两种情况
1.两个节点一个是另一个的祖先节点
2.两个节点不互为祖先节点
我们来看一下图:
在这里插入图片描述

//好一点的算法  但是理解较难
	public static Node theSameParentBetter(Node head,Node node1,Node node2){
		if(head==null || head==node1 || head== node2)//如果当前节点是空或者是 node1 或者是 node2 就直接返回
			return head;
		Node left=theSameParentBetter(head.left,node1,node2);//向左树和右树要节点
		Node right=theSameParentBetter(head.right,node1,node2);
		if(left!=null&&right!=null){//如果左右同时不为空 返回当前节点
			return head;
		}
		return left!=null ? left: right;//返回不为空的节点 如果都为null就返回null
	}
  1. 寻找后节点 中序遍历 一个节点后面的节点就是它的后继节点,如果有一个如下的数据结构能否缩减它的时间(多了一个记录父节点的值):
public static class NodeTemp{
		int val;
		NodeTemp left;
		NodeTemp right;
		NodeTemp parent;//多了一个记录父节点的值
		public NodeTemp(int val){
			this.val=val;
		}
		@Override
		public String toString() {
			return "NodeTemp [val=" + val + "]";
		}
	}

如果我们要找出一个节点的后继节点,根据中序遍历有以下两种情况:
1.如果一个节点没有右树, 那么它的后继节点就是它右孩子的最左边的节点:在这里插入图片描述
2.如果没有右树,那么它的后继节点就是它所有父节点中第一个是它左子树为另一个父节点的节点:
在这里插入图片描述

//后继节点
	public static NodeTemp nextNode(NodeTemp node){
		if(node==null)
			return null;
		//1.首先判断它有无右树 如果有 那么后继节点就是它有孩子最左边的子树
		if(node.right!=null){
			NodeTemp temp=node.right;
			while(temp.left!=null){
				temp=temp.left;
			}
			return temp;
		}
		//2.如果它没有右树 那么后继节点就是 他的所有父节点中 第一个是它的父节点的左子树的节点
		NodeTemp temp=node;
		NodeTemp parent=node.parent;
		while(parent.left!=temp && parent!=null){//如果父类为null的话 那么就说明它的后继节点为null
			temp=temp.parent;
			parent=temp.parent;
		}
		return parent;
	}
  1. 树的序列化与反序列化 就是一个树可以编译成一个唯一的东西,反序列化就是通过这个唯一的东西能够获得以前的树
    先序中序后序都可以完成,这里我们用先序:
    我们遇到null的时候我们就返回 #, 其他值就直接返回 值加上一个, 所以代码如下
//先序的序列化
	public static String firstSerialization(Node head){
		if(head==null)
			return "#,";
		return head.val+","+firstSerialization(head.left)+firstSerialization(head.right);
	}

反序列化我么只需要将对象装入队列中,通过先序遍历获得对象即可:

//先序的反序列化
	public static Node firstUnserialize(Queue<String> queue){
		String value = queue.poll();
		if(value.equals("#"))
			return null;
		Node node1=new Node(value);
		node1.left = firstUnserialize(queue);
		node1.right = firstUnserialize(queue);
		return node1;
	}
  1. 一张纸对折 如果说面对着我们向里面凹的是凹折痕 凸的是凸折痕 给你一个数N,从上到下打印是凹还是凸。
    这个不太好解释 反正最后,你根据每一次折下来的标记把第几次对折当作树的第几层,可以得到以下的树:在这里插入图片描述
public static void fold(int i,int N,boolean down){
		if(i>N)
			return;
		fold(i+1,N,true);
		System.out.println(down?"凹":"凸");
		fold(i+1,N,false);
	}

如果有时间的话 去看一下左程云大佬的视频 它讲的很清楚。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值