java实现树的一般操作
https://www.cnblogs.com/dawnyxl/p/9047437.html
树是数据结构中最基本的结构,今天的博客更新一下树的基本操作:
树的节点结构:

package tree; /**
* TreeNode: 普通的树节点
* @author xuejupo jpxue@travelsky.com
* create in 2015-11-19 下午5:30:31
*
*/
public class TreeNode<T> {
T value; TreeNode<T> leftChild;
TreeNode<T> rightChild; TreeNode(T value) {
this.value = value;
}
TreeNode() {
} /** 增加左子节点
* addLeft:
* @param value
* void 返回类型
*/
public void addLeft(T value){
TreeNode<T> leftChild = new TreeNode<T>(value);
this.leftChild = leftChild;
}
/**
* addRight: 增加右子节点
* @param value
* void 返回类型
*/
public void addRight(T value){
TreeNode<T> rightChild = new TreeNode<T>(value);
this.rightChild = rightChild;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
* 重载equal方法
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(!(obj instanceof TreeNode)){
return false;
}
return this.value.equals(((TreeNode<?>)obj).value);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
* 重载hashCode方法
*/
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.value.hashCode();
}
@Override
public String toString(){
return this.value==null?"":this.value.toString();
} }

树的基本操作类:

package tree; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue; /**
* TreeTools:树的操作类
*
* @author xuejupo jpxue@travelsky.com
*
* create in 2015-11-19 下午5:31:05
*
*/
public class TreeTools { /**
* getTreeNum: 判断树中节点个数
*
* @param root
* 根节点
* @return int 返回类型
*/
public static <T> int getTreeNum(TreeNode<T> root) {
if (root == null) {
return 0;
}
return getTreeNum(root.leftChild) + getTreeNum(root.rightChild) + 1;
} /**
* getTreeDepth: 判断树的深度
*
* @param root
* 根节点
* @return int 返回类型
*/
public static <T> int getTreeDepth(TreeNode<T> root) {
if (root == null) {
return 0;
}
int leftDepth = getTreeDepth(root.leftChild) + 1;
int rightDepth = getTreeDepth(root.rightChild) + 1;
return Math.max(leftDepth, rightDepth);
} /**
* preOrderTravel: 前序遍历
*
* @param root
* void 返回类型
*/
public static <T> void preOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
visitNode(root);
preOrderTravel(root.leftChild);
preOrderTravel(root.rightChild);
} /**
* midOrderTravel: 中序遍历
*
* @param root
* void 返回类型
*/
public static <T> void midOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
midOrderTravel(root.leftChild);
visitNode(root);
midOrderTravel(root.rightChild);
} /**
* backOrderTravel: 后序遍历
*
* @param root
* void 返回类型
*/
public static <T> void backOrderTravel(TreeNode<T> root) {
if (root == null) {
return;
}
backOrderTravel(root.leftChild);
backOrderTravel(root.rightChild);
visitNode(root);
} /**
* visitNode: 访问node节点
*
* @param node
* void 返回类型
*/
private static <T> void visitNode(TreeNode<T> node) {
System.out.print(node.value + "\t");
} /**
* levelTravel: 分层遍历
*
* @param root
* void 返回类型
*/
public static <T> void levelTravel(TreeNode<T> root) {
Queue<TreeNode<T>> q = new LinkedList<TreeNode<T>>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode<T> temp = q.poll();
visitNode(temp);
if (temp.leftChild != null) {
q.offer(temp.leftChild);
}
if (temp.rightChild != null) {
q.offer(temp.rightChild);
}
}
} /**
* getNumForKlevel: 求第K层节点个数
*
* @param root
* @param k
* @return int 返回类型
*/
public static <T> int getNumForKlevel(TreeNode<T> root, int k) {
if (root == null || k < 1) {
return 0;
}
if (k == 1) {
return 1;
}
int leftNum = getNumForKlevel(root.leftChild, k - 1);
int rightNum = getNumForKlevel(root.rightChild, k - 1);
return leftNum + rightNum;
} /**
* getLeafNum: 求二叉树中叶子节点的个数
*
* @param root
* @return int 返回类型
*/
public static <T> int getLeafNum(TreeNode<T> root) {
if (root == null) {
return 0;
}
if (root.leftChild == null && root.rightChild == null) {
return 1;
}
int leftNum = getLeafNum(root.leftChild);
int rightNum = getLeafNum(root.rightChild);
return leftNum + rightNum;
} /**
* exchange: 交换根节点的左右子树
*
* @param root
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> exchange(TreeNode<T> root) {
if (root == null) {
return null;
}
TreeNode<T> left = exchange(root.leftChild);
TreeNode<T> right = exchange(root.rightChild);
root.leftChild = right;
root.rightChild = left;
return root;
} /**
* nodeIsChild: 查看node是否是root的子节点
*
* @param root
* @param node
* @return boolean 返回类型
*/
public static <T> boolean nodeIsChild(TreeNode<T> root, TreeNode<T> node) {
if (root == null || node == null) {
return false;
}
if (root == node) {
return true;
}
boolean isFind = nodeIsChild(root.leftChild, node);
if (!isFind) {
isFind = nodeIsChild(root.rightChild, node);
}
return isFind;
} /**
* findAllFatherNode: 返回两个节点lnode和rnode的以root为根节点的公共父节点
*
* @param root
* 根节点
* @param lNode
* @param rNode
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> findAllFatherNode(TreeNode<T> root,
TreeNode<T> lNode, TreeNode<T> rNode) {
if (lNode == root || rNode == root) {
return root;
}
if (root == null || lNode == null || rNode == null) {
return null;
}
// 如果lNode是左子树的节点
if (nodeIsChild(root.leftChild, lNode)) {
if (nodeIsChild(root.rightChild, rNode)) {
return root;
} else {
return findAllFatherNode(root.leftChild, lNode, rNode);
}
} else {
if (nodeIsChild(root.leftChild, rNode)) {
return root;
} else {
return findAllFatherNode(root.rightChild, lNode, rNode);
}
}
} /**
* getTreeFromPreAndMid: 根据前序和中序构建二叉树
*
* @param pre
* 前序序列
* @param mid
* 中序序列
* @return TreeNode 返回类型
*/
public static <T> TreeNode<T> getTreeFromPreAndMid(List<T> pre, List<T> mid) {
if (pre == null || mid == null || pre.size() == 0 || mid.size() == 0) {
return null;
}
if (pre.size() == 1) {
return new TreeNode<T>(pre.get(0));
}
TreeNode<T> root = new TreeNode<T>(pre.get(0));
// 找出根节点在中序中的位置
int index = 0;
while (!mid.get(index++).equals(pre.get(0))) {
}
// 构建左子树的前序
List<T> preLeft = new ArrayList<T>(index);
// 左子树的中序
List<T> midLeft = new ArrayList<T>(index);
for (int i = 1; i < index; i++) {
preLeft.add(pre.get(i));
}
for (int i = 0; i < index - 1; i++) {
midLeft.add(mid.get(i));
} // 重建左子树
root.leftChild = getTreeFromPreAndMid(preLeft, midLeft);
// 右子树的前序
List<T> preRight = new ArrayList<T>(pre.size() - index - 1);
// 右子树的中序
List<T> midRight = new ArrayList<T>(pre.size() - index - 1);
for (int i = 0; i <= pre.size() - index - 1; i++) {
preRight.add(pre.get(index + i));
}
for (int i = 0; i <= pre.size() - index - 1; i++) {
midRight.add(mid.get(index + i));
}
// 重建→子树
root.rightChild = getTreeFromPreAndMid(preRight, midRight);
return root;
} /**
* equals: 查看node1和node2两棵树是否相等(两棵树所有节点都相等)
*
* @param node1
* node2 两个节点
* @return boolean 返回类型
*/
public static <T> boolean equals(TreeNode<T> node1, TreeNode<T> node2) {
// TODO Auto-generated method stub
if (node1 == null && node2 == null) {
return true;
} else if (node1 == null || node2 == null) {
return false;
}
boolean isEqual = node1.value.equals(node2.value);
boolean isLeftEqual = equals(node1.leftChild, node2.leftChild);
boolean isRightEqual = equals(node1.rightChild, node2.rightChild);
return isEqual && isLeftEqual && isRightEqual;
}
}

测试类:
测试类中,先利用节点中的基本操作构建一棵树:

public static void main(String[] args) {
// TODO Auto-generated method stub
TreeNode<Integer> t = new TreeNode<Integer>(1);
t.addLeft(2);
t.addRight(3);
t.leftChild.addLeft(4);
t.leftChild.addRight(5);
System.out.println("中序遍历测试:");
TreeTools.midOrderTravel(t);
System.out.println("\n前序遍历测试:");
TreeTools.preOrderTravel(t);
System.out.println("\n后序遍历测试:");
TreeTools.backOrderTravel(t);
System.out.println("\n层次遍历测试:");
TreeTools.levelTravel(t);
System.out.println("\n树的深度:"+TreeTools.getTreeDepth(t));
System.out.println("树的叶子个数:"+TreeTools.getLeafNum(t));
System.out.println("树的节点个数:"+TreeTools.getTreeNum(t));
System.out.println("第2层节点个数为:"+TreeTools.getNumForKlevel(t,2));
List<Integer> pre = new ArrayList<Integer>();
pre.add(1);
pre.add(2);
pre.add(4);
pre.add(5);
pre.add(3);
List<Integer> mid = new ArrayList<Integer>();
mid.add(4);
mid.add(2);
mid.add(5);
mid.add(1);
mid.add(3);
TreeNode<Integer> root = TreeTools.getTreeFromPreAndMid(pre, mid);
System.out.println("\n通过前序和中序构建树测试:");
TreeTools.levelTravel(root);
System.out.println("\n构建的树比较测试:");
System.out.println(TreeTools.equals(t,root));
}

结果为:

中序遍历测试:
4 2 5 1 3
前序遍历测试:
1 2 4 5 3
后序遍历测试:
4 5 2 3 1
层次遍历测试:
1 2 3 4 5
树的深度:3
树的叶子个数:3
树的节点个数:5
第2层节点个数为:2 通过前序和中序构建树测试:
1 2 3 4 5
构建的树比较测试:
true

最后的话: 树的基本操作都很简单,树是我见过的最适合用递归来操作的数据结构了。因为子节点和父节点是一样的类型,而且基本具有同样的属性。
以上的方法里面,可能只有利用前序和中序构建一棵树不容易理解。 比如图1里面那棵树,前序是1 2 4 5 3 中序是4 2 5 1 3 前序的1表示根是1,然后在中序序列里,1左边的是根节点的左子树,1后边的是整棵树的右子树,中序1前边有3个数字,右边有1个数字,表示根节点的左子树有3个节点,右子树有1个节点,所以构建根节点左子树的前序和中序时,取根节点前序的2,4,5和根节点中序的425,构建根节点右子树的前序和中序时取根节点的3,然后递归构建即可。
java实现树的一般操作的更多相关文章
- hdu 3436 线段树 一顿操作
Queue-jumpers Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- 读Hadoop3.2源码,深入了解java调用HDFS的常用操作和HDFS原理
本文将通过一个演示工程来快速上手java调用HDFS的常见操作.接下来以创建文件为例,通过阅读HDFS的源码,一步步展开HDFS相关原理.理论知识的说明. 说明:本文档基于最新版本Hadoop3.2. ...
- java的基础知识文件操作和标识符
1.文件夹的操作 dir :显示当前文件夹中的所有文件和文件夹. cd 路径: 进入到指定的路径. cd .. : 回到上一级目录 cd \ : 回到当前目录的跟目录 md 文件夹名 创建一个 ...
- Elasticsearch【JAVA REST Client】客户端操作
ES系统作为集群,环境搭建非常方便简单. 现在在我们的应用中,如何对这个集群进行操作呢? 我们利用ES系统,通常都是下面的架构: 在这里,客户端的请求通过LB进行负载均衡,因为操作任何一个ES的实例, ...
- Java Calendar 类的时间操作
Java Calendar 类的时间操作 标签: javaCalendar时间Date 2013-07-30 17:53 140401人阅读 评论(7) 收藏 举报 分类: 所有(165) Java ...
- 更新java对xml文件的操作
//更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...
- Java 字节流实现文件读写操作(InputStream-OutputStream)
Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...
- Java API —— IO流(数据操作流 & 内存操作流 & 打印流 & 标准输入输出流 & 随机访问流 & 合并流 & 序列化流 & Properties & NIO)
1.操作基本数据类型的流 1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出 ...
- java遍历树(深度遍历和广度遍历
java遍历树如现有以下一颗树:A B B1 B11 B2 B22 C C ...
随机推荐
- 201604-1 折点计数 Java
思路: 这个题要小心考虑不全.左右两边都比这个数小 或者 左右两边都比这个数大 import java.util.Scanner; public class Main { public static ...
- 第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第二天】
淘淘商城(SpringMVC+Spring+Mybatis) 是传智播客在2015年9月份录制的,几年过去了.由于视频里课上老师敲的代码和项目笔记有些细节上存在出入,只有根据日志报错信息作出适当的调 ...
- Python重学记录1
写下这个标题觉得可笑,其实本人2014年就自学过一次python,当时看的是中谷教育的milo老师的视频,也跟着写了一些代码,只是因为当时工作上用不到也就淡忘了.不过说实话当时的水平也很低下,本来也没 ...
- Properties in Algebra
附录-Properties in Algebra 部分证明转载自标注,仅作个人整理查阅用. 范数 (norm) \(^{[1]}\) 要更好的理解范数,就要从函数.几何与矩阵的角度去理解,我尽量讲的通 ...
- 关于Weblogic的知识点
一.解决Weblogic域创建.启动.进入控制台慢问题 搭建Weblogic 11g和12c环境时发现,安装正常,以默认组件安装,但是创建域的时候特别慢,一般需要几分钟至10分钟,卡在“创建域安全信息 ...
- ant design for vue 关于table的一些问题
1.为table添加分页: :pagination="pagination" pagination: { defaultPageSize: 10, showTotal: (tota ...
- linux中awk的应用
1.awk的基本认识和使用方法,参考下面链接 https://www.cnblogs.com/timxgb/p/4658631.html 2.awk中关于条件判断的用法,如 https://blog. ...
- look and say 外观数列的python实现
#look_and_say 外观数列 如果我们把 1 作为Look-and-say 数列的第一项,那么,它的前几项是这样的: 1, 11, 21, 1211, 111221, 312211, 1311 ...
- StartDT AI Lab | 智能运筹助力企业提升决策效率、优化决策质量
在人工智能和大数据时代,越来越多的云上数据和越来越智能的模型开始辅助人们做出各种最优决策,从运营效率.成本节约.最优配置等方方面面,实现降本增效,进一步提升商业效率.京东.美团.滴滴.顺丰等众多知名厂 ...
- 吴裕雄--天生自然TensorFlow高层封装:Keras-返回值
# 1. 数据预处理. import keras from keras.models import Model from keras.datasets import mnist from keras. ...