Java中二叉树存储结构实现
一、二叉树
二叉树指的是每个节点最多只能有两个子树的有序树。通常左边的子树被称为“左子树”(left subtree),右边的子树被称为右子树。
二叉树的每个节点最多只有2棵子树,二叉树的子树次序不能颠倒。
二、顺序存储二叉树的实现
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class ArrayBinTree<T> { // 使用数组来记录该树的所有节点
private Object[] datas;
private int DEFAULT_DEEP = 8;
// 保存该树的深度
private int deep;
private int arraySize; // 以默认的深度创建二叉树
public ArrayBinTree() {
this.deep = DEFAULT_DEEP;
this.arraySize = (int) (Math.pow(2, deep) - 1);
datas = new Object[arraySize];
} // 以指定深度创建二叉树
public ArrayBinTree(int deep) {
this.deep = deep;
this.arraySize = (int) Math.pow(2, deep) - 1;
datas = new Object[arraySize];
} // 以指定深度、指定节点创建二叉树
public ArrayBinTree(int deep, T data) {
this.deep = deep;
this.arraySize = (int) Math.pow(2, deep) - 1;
datas = new Object[arraySize];
datas[0] = data;
} /**
* 为指定节点添加子节点
*
* @param index 需要添加子节点的父节点的索引
* @param data 新子节点的数据
* @param left 是否为左节点
*/
public void add(int index, T data, boolean left) {
if (datas[index] == null) {
throw new RuntimeException(index + "处节点为空,无法添加子节点");
}
if (2 * index + 1 >= arraySize) {
throw new RuntimeException("树底层的数组已满,树越界异常");
}
// 添加左子节点
if (left) {
datas[2 * index + 1] = data;
} else {
datas[2 * index + 2] = data;
}
} // 判断二叉树是否为空
public boolean empty() {
// 根据根元素判断二叉树是否为空
return datas[0] == null;
} // 返回根节点
public T root() {
return (T) datas[0];
} // 返回指定节点(非根结点)的父节点
public T parent(int index) {
return (T) datas[(index - 1) / 2];
} // 返回指定节点(非叶子)的左子节点,当左子节点不存在时返回null
public T left(int index) {
if (2 * index + 1 >= arraySize) {
throw new RuntimeException("该节点为叶子节点,无子节点");
}
return (T) datas[index * 2 + 1];
} // 返回指定节点(非叶子)的右子节点,当右子节点不存在时返回null
public T right(int index) {
if (2 * index + 1 >= arraySize) {
throw new RuntimeException("该节点为叶子节点,无子节点");
}
return (T) datas[index * 2 + 2];
} // 返回该二叉树的深度
public int deep(int index) {
return deep;
} // 返回指定节点的位置
public int pos(T data) {
// 该循环实际上就是按广度遍历来搜索每个节点
for (int i = 0; i < arraySize; i++) {
if (datas[i].equals(data)) {
return i;
} }
return -1;
} public String toString() {
return java.util.Arrays.toString(datas);
} }
测试类:
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class ArrayBinTreeTest { public static void main(String[] args) { ArrayBinTree<String> binTree = new ArrayBinTree<String>(4, "根");
binTree.add(0, "第二层右子节点", false);
binTree.add(2, "第三层右子节点", false);
binTree.add(6, "第四层右子节点", false);
System.out.println(binTree); } }
程序输出:
[根, null, 第二层右子节点, null, null, null, 第三层右子节点, null, null, null, null, null, null, null, 第四层右子节点]
三、二叉树的二叉链表存储
二叉链表存储的思想是让每个节点都能“记住”它的左、右两个子节点。为每个节点增加left、right两个指针,分别引用该节点的左、右两个子节点。
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class TwoLinkBinTree<E> { public static class TreeNode { Object data;
TreeNode left;
TreeNode right; public TreeNode() { } public TreeNode(Object data) {
this.data = data;
} public TreeNode(Object data, TreeNode left, TreeNode right) {
this.data = data;
this.left = left;
this.right = right;
} } private TreeNode root; // 以默认的构造器创建二叉树
public TwoLinkBinTree() {
this.root = new TreeNode();
} // 以指定根元素创建二叉树
public TwoLinkBinTree(E data) {
this.root = new TreeNode(data);
} /**
* 为指定节点添加子节点
*
* @param parent 需要添加子节点的父节点的索引
* @param data 新子节点的数据
* @param isLeft 是否为左节点
* @return 新增的节点
*/
public TreeNode addNode(TreeNode parent, E data, boolean isLeft) { if (parent == null) {
throw new RuntimeException(parent + "节点为null, 无法添加子节点");
}
if (isLeft && parent.left != null) {
throw new RuntimeException(parent + "节点已有左子节点,无法添加左子节点");
}
if (!isLeft && parent.right != null) {
throw new RuntimeException(parent + "节点已有右子节点,无法添加右子节点");
} TreeNode newNode = new TreeNode(data);
if (isLeft) {
// 让父节点的left引用指向新节点
parent.left = newNode;
} else {
// 让父节点的left引用指向新节点
parent.right = newNode;
}
return newNode;
} // 判断二叉树是否为空
public boolean empty() {
// 根据元素判断二叉树是否为空
return root.data == null;
} // 返回根节点
public TreeNode root() {
if (empty()) {
throw new RuntimeException("树为空,无法访问根节点");
}
return root;
} // 返回指定节点(非根节点)的父节点
public E parent(TreeNode node) {
// 对于二叉树链表存储法,如果要访问指定节点的父节点必须遍历二叉树
return null;
} // 返回指定节点(非叶子)的左子节点,当左子节点不存在时返回null
public E leftChild(TreeNode parent) {
if (parent == null) {
throw new RuntimeException(parent + "节点为null,无法添加子节点");
}
return parent.left == null ? null : (E) parent.left.data;
} // 返回指定节点(非叶子)的右子节点,当右子节点不存在时返回null
public E rightChild(TreeNode parent) {
if (parent == null) {
throw new RuntimeException(parent + "节点为null,无法添加子节点");
}
return parent.right == null ? null : (E) parent.right.data;
} // 返回该二叉树的深度
public int deep() {
// 获取该树的深度
return deep(root);
} // 这是一个递归方法:每一棵子树的深度为其所有子树的最大深度 + 1
private int deep(TreeNode node) {
if (node == null) {
return 0;
}
// 没有子树
if (node.left == null && node.right == null) {
return 1;
} else {
int leftDeep = deep(node.left);
int rightDeep = deep(node.right);
// 记录其所有左、右子树中较大的深度
int max = leftDeep > rightDeep ? leftDeep : rightDeep;
// 返回其左右子树中较大的深度 + 1
return max + 1;
} } }
测试类:
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class TwoLinkBinTreeTest { public static void main(String[] args) { TwoLinkBinTree<String> binTree = new TwoLinkBinTree<String>("根节点");
// 依次添加节点
TwoLinkBinTree.TreeNode tn1 = binTree.addNode(binTree.root(), "第二层左节点", true);
TwoLinkBinTree.TreeNode tn2 = binTree.addNode(binTree.root(), "第二层右节点", false);
TwoLinkBinTree.TreeNode tn3 = binTree.addNode(tn2, "第三层左节点", true);
TwoLinkBinTree.TreeNode tn4 = binTree.addNode(tn2, "第三层右节点", false);
TwoLinkBinTree.TreeNode tn5 = binTree.addNode(tn3, "第四层左节点", true); System.out.println("tn2的左子节点:" + binTree.leftChild(tn2));
System.out.println("tn2的右子节点:" + binTree.rightChild(tn2));
System.out.println(binTree.deep()); } }
程序输出:
tn2的左子节点:第三层左节点
tn2的右子节点:第三层右节点
4
四、二叉树的三叉链表存储
三叉链表存储方式是对二叉链表的一种改进,通过为树节点增加一个parent引用,可以让每个节点都能非常方便的访问其父节点,三叉链表存储的二叉树即可方便地向下访问节点,也可方便地向上访问节点。
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class ThreeLinkBinTree<E> { public static class TreeNode { Object data;
TreeNode left;
TreeNode right;
TreeNode parent; public TreeNode() { } public TreeNode(Object data) {
this.data = data;
} public TreeNode(Object data, TreeNode left, TreeNode right, TreeNode parent) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
} } private TreeNode root; // 以默认的构造器创建二叉树
public ThreeLinkBinTree() {
this.root = new TreeNode();
} // 以指定根元素创建二叉树
public ThreeLinkBinTree(E data) {
this.root = new TreeNode(data);
} /**
* 为指定节点添加子节点
*
* @param parent 需要添加子节点的父节点的索引
* @param data 新子节点的数据
* @param isLeft 是否为左节点
* @return 新增的节点
*/
public TreeNode addNode(TreeNode parent, E data, boolean isLeft) { if (parent == null) {
throw new RuntimeException(parent + "节点为null, 无法添加子节点");
}
if (isLeft && parent.left != null) {
throw new RuntimeException(parent + "节点已有左子节点,无法添加左子节点");
}
if (!isLeft && parent.right != null) {
throw new RuntimeException(parent + "节点已有右子节点,无法添加右子节点");
} TreeNode newNode = new TreeNode(data);
if (isLeft) {
// 让父节点的left引用指向新节点
parent.left = newNode;
} else {
// 让父节点的left引用指向新节点
parent.right = newNode;
}
// 让新节点的parent引用到parent节点
newNode.parent = parent;
return newNode;
} // 判断二叉树是否为空
public boolean empty() {
// 根据元素判断二叉树是否为空
return root.data == null;
} // 返回根节点
public TreeNode root() {
if (empty()) {
throw new RuntimeException("树为空,无法访问根节点");
}
return root;
} // 返回指定节点(非根节点)的父节点
public E parent(TreeNode node) {
if (node == null) {
throw new RuntimeException("节点为null,无法访问其父节点");
}
return (E) node.parent.data;
} // 返回指定节点(非叶子)的左子节点,当左子节点不存在时返回null
public E leftChild(TreeNode parent) {
if (parent == null) {
throw new RuntimeException(parent + "节点为null,无法添加子节点");
}
return parent.left == null ? null : (E) parent.left.data;
} // 返回指定节点(非叶子)的右子节点,当右子节点不存在时返回null
public E rightChild(TreeNode parent) {
if (parent == null) {
throw new RuntimeException(parent + "节点为null,无法添加子节点");
}
return parent.right == null ? null : (E) parent.right.data;
} // 返回该二叉树的深度
public int deep() {
// 获取该树的深度
return deep(root);
} // 这是一个递归方法:每一棵子树的深度为其所有子树的最大深度 + 1
private int deep(TreeNode node) {
if (node == null) {
return 0;
}
// 没有子树
if (node.left == null && node.right == null) {
return 1;
} else {
int leftDeep = deep(node.left);
int rightDeep = deep(node.right);
// 记录其所有左、右子树中较大的深度
int max = leftDeep > rightDeep ? leftDeep : rightDeep;
// 返回其左右子树中较大的深度 + 1
return max + 1;
} } }
测试类:
package com.ietree.basic.datastructure.tree.binarytree; /**
* Created by ietree
* 2017/5/1
*/
public class ThreeLinkBinTreeTest { public static void main(String[] args) { ThreeLinkBinTree<String> binTree = new ThreeLinkBinTree<String>("根节点");
// 依次添加节点
ThreeLinkBinTree.TreeNode tn1 = binTree.addNode(binTree.root(), "第二层左节点", true);
ThreeLinkBinTree.TreeNode tn2 = binTree.addNode(binTree.root(), "第二层右节点", false);
ThreeLinkBinTree.TreeNode tn3 = binTree.addNode(tn2, "第三层左节点", true);
ThreeLinkBinTree.TreeNode tn4 = binTree.addNode(tn2, "第三层右节点", false);
ThreeLinkBinTree.TreeNode tn5 = binTree.addNode(tn3, "第四层左节点", true); System.out.println("tn2的左子节点:" + binTree.leftChild(tn2));
System.out.println("tn2的右子节点:" + binTree.rightChild(tn2));
System.out.println(binTree.deep()); } }
程序输出:
tn2的左子节点:第三层左节点
tn2的右子节点:第三层右节点
4
Java中二叉树存储结构实现的更多相关文章
- 存储结构与邻接矩阵,深度优先和广度优先遍历及Java实现
如果看完本篇博客任有不明白的地方,可以去看一下<大话数据结构>的7.4以及7.5,讲得比较易懂,不过是用C实现 下面内容来自segmentfault 存储结构 要存储一个图,我们知道图既有 ...
- 【Java数据结构学习笔记之一】线性表的存储结构及其代码实现
应用程序后在那个的数据大致有四种基本的逻辑结构: 集合:数据元素之间只有"同属于一个集合"的关系 线性结构:数据元素之间存在一个对一个的关系 树形结构:数据元素之间存在一个对多个关 ...
- Java队列存储结构及实现
一.队列(Queue) 队列是一种特殊的线性表,它只允许在表的前段(front)进行删除操作,只允许在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头. 对于一个队 ...
- 牛客网Java刷题知识点之HashMap的实现原理、HashMap的存储结构、HashMap在JDK1.6、JDK1.7、JDK1.8之间的差异以及带来的性能影响
不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号: 大数据躺过的坑 Java从入门到架构师 人工智能躺过的坑 ...
- Java中Map<Key, Value>存储结构根据值排序(sort by values)
需求:Map<key, value>中可以根据key, value 进行排序,由于 key 都是唯一的,可以很方便的进行比较操作,但是每个key 对应的value不是唯一的,有可能出现多个 ...
- Java数据结构——树的三种存储结构
(转自http://blog.csdn.net/x1247600186/article/details/24670775) 说到存储结构,我们就会想到常用的两种存储方式:顺序存储和链式存储两种. 先来 ...
- java资料——顺序存储结构和链式存储结构(转)
顺序存储结构 主要优点 节省存储空间,随机存取表中元素 缺 点 插入和删除操作需要移动元素 在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构. 顺序存储结 ...
- Java栈之链式栈存储结构实现
一.链栈 采用单链表来保存栈中所有元素,这种链式结构的栈称为链栈. 二.栈的链式存储结构实现 package com.ietree.basic.datastructure.stack; /** * 链 ...
- Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构
Atitit.数据索引 的种类以及原理实现机制 索引常用的存储结构 1. 索引的分类1 1.1. 按照存储结构划分btree,hash,bitmap,fulltext1 1.2. 索引的类型 按查找 ...
随机推荐
- JAVA中所有与集合有关的实现类都是这六个接口的实现类
JAVA中所有与集合有关的实现类都是这六个接口的实现类. Collection接口:集合中每一个元素为一个对象,这个接口将这些对象组织在一起,形成一维结构. List接口代表按照元素一定的相关顺序来组 ...
- oracle oci 调用 1
http://blog.163.com/earth_of_fire/blog/static/1368943200791211622278/(总结) http://blog.163.com/earth_ ...
- 【BZOJ】1064: [Noi2008]假面舞会(判环+gcd+特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=1064 表示想到某一种情况就不敢写下去了.... 就是找环的gcd...好可怕.. 于是膜拜了题解.. ...
- Android SDK代理server解决国内不能更新下载问题
读者须知:本篇文章中最靠谱的是第三种方式,近期有读者反映第三种方式也不行了,以下提供一点其它途径的开源镜像网站: 国内高校的开源镜像站 中国科学技术大学(debian.ustc.edu.cn) 上海交 ...
- 指针*pbuffer和getchar 读取字符串
在C语言入门教材里看到这一段代码,没看懂是什么意思.char buffer[10];char *pbuffer = buffer;while( (*pbuffer++ = getchar() )!= ...
- android 清除缓存功能
本应用数据清除管理器 DataCleanManager.java 是从网上摘的 忘了 名字了 对不住了 载入一个webview 产生缓存 众所周知的webview是产生缓存的主要原因之中的一 ...
- 剑指 offer set 21 圆圈中最后剩下的数字
思路 1. 经典解法是用环形链表模拟圆圈, 然后每次减少一个节点. 时间复杂度为 o(mn), 空间复杂度为 o(n) 2. 转化成数学问题, 递推公式决定下一个元素. 时间复杂度为 o(n), 空间 ...
- ajax的轮询和长轮询
概念: 轮询(polling):客户端按规定时间定时像服务端发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 概念总是枯燥的,只有代码方能解心头之快 前段代码:index.html: & ...
- GIF动画录制工具(写教程时用的比较小巧的gif工具)
1 软件小巧实用,只有1m 2 gif效果还可以 3 绿色,无需安装 很多地方能下载,百度就行. 下载地址: http://www.downxia.com/downinfo/41427.html
- 使用Mac App Store更新、下载软件时出现未知错误的解决方法
很多果迷在使用 Mac App Store 更新/下载软件时,可能都曾被”未知错误”困扰过,怎么解决也不行.然而,过一段时间不知道做了什么又自己好了.今天我们提供两个解决这个问题的方法,下次遇到这个问 ...