死磕算法-树相关

在二叉树中找到一个节点的后继节点

题目:现在有一种新的二叉树节点类型如下

public static class Node {
		public int value;
		public Node left;
		public Node right;
		public Node parent;

		public Node(int data) {
			this.value = data;
		}
	}

该结构比普通二叉树节点结构多了一个指向父节点的parent指针。假 设有一 棵Node类型的节点组成的二叉树,树中每个节点的parent指针 都正确地指向 自己的父节点,头节点的parent指向null。只给一个在 二叉树中的某个节点 node,请实现返回node的后继节点的函数。在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点

在这里插入图片描述
该二叉树的中序遍历为:7425136
则7的后继节点为4,3的后继节点为6,6则存在后继节点。

当某个节点存在右子树时,该节点的后继节点为该节点右子树的最左节点,如2的后继节点为5,3的后继节点为6;若该节点不存在右子树,则将当前节点设为x,寻找存在使x为parent节点的左孩子为止

当前节点不存在右子树,如何寻找后继节点?
假设寻找7的后继节点:
在这里插入图片描述
parent节点为x的后继节点,因此4为7的后继节点。
在这里插入图片描述
寻找5的后继节点,可是x不是Parent节点中的左孩子,将Parent节点设为原Parent节点的Parent节点,这里2的Parent节点为1,将x节点设为原来的Parent节点,即x换为2,因此发现2是1的左孩子,所以节点5的后继节点是1

public static Node getSuccessorNode(Node node) {
		if (node == null) {
			return node;
		}
		//如果存在右孩子,则找到最左节点
		if (node.right != null) {
			return getLeftMost(node.right);
		} else {
			//直到parent为null或者parent.left = node
			Node parent = node.parent;
			while (parent != null && parent.left != node) {
				node = parent;
				parent = node.parent;
			}
			return parent;
		}
	}

	//寻找最左节点
	public static Node getLeftMost(Node node) {
		if (node == null) {
			return node;
		}
		while (node.left != null) {
			node = node.left;
		}
		return node;
	}

二叉树的序列化和反序列化

题目:首先我们介绍二叉树先序序列化的方式,假设序列化的结果字符串为str,初始时str等于空字符串。先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。如果遇到不为空的节点,假设节点值为3,就在str的末尾加上“3!”。现在请你实现树的先序序列化

思路:我们知道所谓的二叉树是一种由对象引用将多个结点关联起来的抽象数据结构,是存在于内存中的,不能进行持久化,如果需要将一颗二叉树的结构持久化保存,需要将其转换为字符串并保存到文件中,于是关键是建立一套规则,使得二叉树可以与字符串一一对应,根据一个二叉树可以唯一的得到一个字符串,根据一个字符串也可以唯一的还原出一棵二叉树

先序遍历序列化
public static String serialByPre(Node head) {
	if (head == null) {
		return "#!";
	}
	String res = head.value + "!";
	res += serialByPre(head.left);
	res += serialByPre(head.right);
	return res;
}
先序遍历反序列化
public static Node reconByPreString(String preStr) {
		//分隔成String数组
		String[] values = preStr.split("!");
		Queue<String> queue = new LinkedList<String>();
		for (int i = 0; i != values.length; i++) {
			queue.offer(values[i]);
		}
		return reconPreOrder(queue);
	}

public static 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;
	}

判断一棵树是否为平衡二叉树

平衡二叉树的提出就是为了保证树不至于太倾斜,尽量保证两边平衡。平衡二叉树要么是一棵空树,要么保证左右子树的高度之差不大于 1,子树也必须是一颗平衡二叉树。

基本思路:递归遍历二叉树的每一个节点来,求出每个节点的左右子树的高度,如果每个节点的左右子树高度差不超过一,按照定义,它就是一棵平衡二叉树。即先判断根节点左右的子树,如果左右子树各自平衡,再合起来判断左右子树是否平衡。

public class IsBalancedTree {
	
	
	public static class Node {
		public Node left;
		public Node right;
		public Integer value;
		
		public Node(Integer value) {
			this.value=value;
		}
	}
	
	public static class ReturnData{
		public boolean isB;
		public Integer height;
		
		public ReturnData(boolean isB , Integer height){
			this.isB=isB;
			this.height=height;
		}
		
		
	}
	
	public static boolean isBalance(Node head) {
		return process(head).isB;
	}
	
	
	public static ReturnData process(Node head) {
		if(head == null) {
			return new ReturnData(true,0);
		}
		
		//左树不平衡则返回
		ReturnData leftData = process(head.left);
		if(!leftData.isB) {
			return new ReturnData(false,0);
		}
		
		//右树不平衡则返回
		ReturnData rightData = process(head.right);
		if(!rightData.isB) {
			return new ReturnData(false,0);
		}
		//左右树不平衡则返回
		if(Math.abs(leftData.height - rightData.height) > 1) {
			return new ReturnData(false,0);
		}
		//返回平衡结果和树的高度
		return new ReturnData(true,Math.max(leftData.height, rightData.height) + 1);
	}
}

判断一棵树是否为搜索二叉树

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。通常下搜索二叉树不存在重复节点

二叉树中序遍历的结果下是依次升序的

在中序遍历时,保存前驱节点,如果当前节点小于前驱结点,则这棵树不是BST

public static int inOrderRecur(Node head) {
		Node pre = head;
		boolean flag=true;
		//左->根->右
		if (head == null) {
			return;
		}
		inOrderRecur(head.left);
		System.out.print(head.value + " ");
		//搜索二叉树的中序遍历是升序
		if(!head.value > pre.value){
			flag = false;
		}
		if(!flag){
			return false;
		}
		inOrderRecur(head.right);
		return true;
	}


判断一棵树是否为完全二叉树

完全二叉树的特点:

  1. 当一个节点有右孩子,但是无左孩子,直接返回false
  2. 当一个节点有左孩子无右孩子,那么接下来要遍历的节点必须是叶子节点(叶子节点左右孩子为空)
  3. 当一个节点是叶子节点,那么按照层序遍历的结果,这个节点之后的所有结点都必须是叶子节点这棵树才是完全二叉树

首先每个节点都会有以下4种情况:
情况一:
在这里插入图片描述
情况二:
在这里插入图片描述
情况三:
在这里插入图片描述
情况四:

在这里插入图片描述

我们开始寻找规律:

  1. 如果当前访问的节点的左右孩子是情况3,说明不是完全二叉树,直接返回false
  2. 如果当前访问的节点的左右孩子是情况1,继续访问其他节点
  3. 如果当前访问的节点的左右孩子是情况2或者情况4,那么我们定义一个状态(接下来访问的所有节点必须全部是叶子节点)。只要遇到情况2或者情况4,这个状态就开启了

使用层次遍历并检查倒数第二层是否满以及是否有元素有右孩子无左孩子即可
二叉树的层序遍历
在这里插入图片描述

层次遍历的遍历顺序:1–>2–>3–>4–>5–>6–>7

public static boolean isCBT(Node head) {
		if (head == null) {
			return true;
		}
		Queue<Node> queue = new LinkedList<Node>();
		boolean leaf = false;
		Node l = null;
		Node r = null;
		queue.offer(head);
		while (!queue.isEmpty()) {
			head = queue.poll();
			l = head.left;
			r = head.right;
			//有右孩子而无左孩子,直接返回false
			//叶节点开启模式下,存在非叶节点,直接返回false
			if ((leaf && (l != null || r != null)) || (l == null && r != null)) {
				return false;
			}
			//压入左孩子
			if (l != null) {
				queue.offer(l);
			}
			//压入右孩子
			if (r != null) {
				queue.offer(r);
			}//有左孩子无右孩子,开启叶节点模式
			else {
				leaf = true;
			}
		}
		return true;
	}

已知一棵完全二叉树,求其节点的个数

回忆下完全二叉树的定义:

设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,
第 h 层所有的结点都连续集中在最左边。

如果一棵满二叉树的高度为L,则节点个数为2L-1
所以,对于完全二叉树,其总是满足以下两种情形:

  1. node的右子树,到达底部,说明node的左子树是满二叉树,如图所示:
    在这里插入图片描述
    上面这种情况,左子树的节点可以直接靠公式算出,右子树需要递归计算

  2. node的右子树,没有到达底部,说明node的右子树是底层高度 - 1 的满二叉树,如图所示:
    在这里插入图片描述
    上面这种情况,右子树的节点个数可以完全靠公式算出,左子树需要靠递归计算

public static int nodeNum(Node head) {
		if (head == null) {
			return 0;
		}
		return bs(head, 1, mostLeftLevel(head, 1));
	}
	
	//l代表节点所在高度
	//h代表该节点下的最深左边界高度
	public static int bs(Node node, int l, int h) {
		if (l == h) {
			return 1;
		}
		//如果右子树的最深左边界到达h,说明左子树为满二叉树,可以直接套公式计算
		if (mostLeftLevel(node.right, l + 1) == h) {
			//左子树为满二叉树计算+右子树递归计算
			return (1 << (h - l)) + bs(node.right, l + 1, h);
		} else {
			//左子树递归计算,右子树为高度-1的满二叉树计算
			return (1 << (h - l - 1)) + bs(node.left, l + 1, h);
		}
	}

	//计算最深左边界
	public static int mostLeftLevel(Node node, int level) {
		while (node != null) {
			level++;
			node = node.left;
		}
		return level - 1;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容介绍 项目结构: Controller层:使用Spring MVC来处理用户请求,负责将请求分发到相应的业务逻辑层,并将数据传递给视图层进行展示。Controller层通常包含控制器类,这些类通过注解如@Controller、@RequestMapping等标记,负责处理HTTP请求并返回响应。 Service层:Spring的核心部分,用于处理业务逻辑。Service层通过接口和实现类的方式,将业务逻辑与具体的实现细节分离。常见的注解有@Service和@Transactional,后者用于管理事务。 DAO层:使用MyBatis来实现数据持久化,DAO层与数据库直接交互,执行CRUD操作。MyBatis通过XML映射文件或注解的方式,将SQL语句与Java对象绑定,实现高效的数据访问。 Spring整合: Spring核心配置:包括Spring的IOC容器配置,管理Service和DAO层的Bean。配置文件通常包括applicationContext.xml或采用Java配置类。 事务管理:通过Spring的声明式事务管理,简化了事务的处理,确保数据一致性和完整性。 Spring MVC整合: 视图解析器:配置Spring MVC的视图解析器,将逻辑视图名解析为具体的JSP或其他类型的视图。 拦截器:通过配置Spring MVC的拦截器,处理请求的预处理和后处理,常用于权限验证、日志记录等功能。 MyBatis整合: 数据源配置:配置数据库连接池(如Druid或C3P0),确保应用可以高效地访问数据库。 SQL映射文件:使用MyBatis的XML文件或注解配置,将SQL语句与Java对象映射,支持复杂的查询、插入、更和删除操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值