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实现树的一般操作的更多相关文章

  1. hdu 3436 线段树 一顿操作

    Queue-jumpers Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  2. 读Hadoop3.2源码,深入了解java调用HDFS的常用操作和HDFS原理

    本文将通过一个演示工程来快速上手java调用HDFS的常见操作.接下来以创建文件为例,通过阅读HDFS的源码,一步步展开HDFS相关原理.理论知识的说明. 说明:本文档基于最新版本Hadoop3.2. ...

  3. java的基础知识文件操作和标识符

    1.文件夹的操作 dir :显示当前文件夹中的所有文件和文件夹. cd 路径:  进入到指定的路径. cd ..  : 回到上一级目录 cd  \ : 回到当前目录的跟目录 md 文件夹名  创建一个 ...

  4. Elasticsearch【JAVA REST Client】客户端操作

    ES系统作为集群,环境搭建非常方便简单. 现在在我们的应用中,如何对这个集群进行操作呢? 我们利用ES系统,通常都是下面的架构: 在这里,客户端的请求通过LB进行负载均衡,因为操作任何一个ES的实例, ...

  5. Java Calendar 类的时间操作

    Java Calendar 类的时间操作 标签: javaCalendar时间Date 2013-07-30 17:53 140401人阅读 评论(7) 收藏 举报 分类: 所有(165) Java ...

  6. 更新java对xml文件的操作

    //更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...

  7. Java 字节流实现文件读写操作(InputStream-OutputStream)

    Java 字节流实现文件读写操作(InputStream-OutputStream) 备注:字节流比字符流底层,但是效率底下. 字符流地址:http://pengyan5945.iteye.com/b ...

  8. Java API —— IO流(数据操作流 & 内存操作流 & 打印流 & 标准输入输出流 & 随机访问流 & 合并流 & 序列化流 & Properties & NIO)

    1.操作基本数据类型的流     1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出 ...

  9. java遍历树(深度遍历和广度遍历

    java遍历树如现有以下一颗树:A     B          B1               B11          B2               B22     C          C ...

随机推荐

  1. html分页自适应居中;css设置分页自适应居中

    制作网页列表的分页必不可少,显示的列表条数也不一样,让我们一起来看看如何让分页标签根据给定的分页自动居中呢. 对<ul>标签设置样式为:{ display: table margin:40 ...

  2. android测量的三种模式

    测量模式有三种引用官方的解释如下 UNSPECIFIED The parent has not imposed any constraint on the child. It can be whate ...

  3. usert

    usert类型 不是一个函数,而是一个语言构造器 usert后会不会释放内存 当usert的文件大于2044KB时才会释放内存,否则不释放内存

  4. python导入自定义的库

    一.导入项目文件夹下的模块 1.导入整个模块 import 模块名 2.导入模块的某个函数 from 模块名 import 函数名 示例 untitled是项目文件夹,文件结构如下 ①在a.py导入c ...

  5. 实践一次有趣的sql优化

    课程表 #课程表 create table Course( c_id int PRIMARY KEY, name varchar(10) ) 增加 100 条数据 #增加课程表100条数据 DROP ...

  6. SVN服务器的搭建(三)

    接下来,试试用TortoiseSVN修改文件,添加文件,删除文件,以及如何解决冲突等. 添加文件 在检出的工作副本中添加一个Readme.txt文本文件,这时候这个文本文件会显示为没有版本控制的状态, ...

  7. 放贷额度相关的ROI计算

    违约模型得到概率估计, 将概率值划分5档, 每一档确定一个授信系数 新的授信 = 每月收入* 授信系数 - 老的授信 计算新增授信额度 计算余额损失

  8. shell并行处理

    for i in (file1 file2 file3), do process_a $i | tee process_a $i_a.txt | process_b > $i_b.txt &am ...

  9. Codeforces 1295D Same GCDs

    题目链接 link Solution 这是一道结论题,有两个做法,分别用了欧拉函数或一点点莫比乌斯反演 (这里只放欧拉函数的做法) 设\(d=gcd(m,a)\) \[gcd(\frac{a}{d}, ...

  10. 802.11X

    LSW1; interface Vlanif100 ip address 192.168.121.2 255.255.255.0连接云的地址 interface GigabitEthernet0/0/ ...