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. 索引的类型 按查找 ...
随机推荐
- 并查集 - UVALive 6889 City Park
City Park Problem's Link: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=129725 Mean: 在 ...
- 【转】社区O2O的增量与存量,机会在哪?
在[O2凹凸社]的前一篇<社区O2O创业百态:三教九流>中总结过目前社区O2O行业的创业氛围,那更进一步看为何有这么多创业者想进入社区O2O市场呢?社区O2O的吸引力在哪?机会又在哪? 一 ...
- (转)Direct3d 设备丢失 (device lost)
1.什么时候设备丢失 一个Direct3D设备, 有两种状态: 操作状态或丢失状态. 操作状态: 是设备的正常状态, 设备按预期运行, 并且能present所有渲染效果 丢失状态: 所有渲染操作悄然失 ...
- hdu 1257 最少拦截系统(动态规划 / 贪心)
最少拦截系统 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Subm ...
- VC++ 轻松实现“闪屏” SplashWnd
我们平时使用的好多软件在运行启动时都会有一个“闪屏”画面显示,一般用于标识软件的一些信息,如软件版本名称.公司等,通过查找资料发现,其实实现起来很简单,一个类就能搞定! SplashWnd.h C+ ...
- Gradle详解+Groovy
http://blog.csdn.net/u014761700/article/details/51867939
- ADT(Android Developer Tools)中配置SVN
1:打开adt-bundle-windows-x86\eclipse目录.新创建目录subclipse (注:adt-bundle-windows-x86 为我的eclipse目录名) 2: 打开Ec ...
- LinCode落单的数
easy 落单的数 查看执行结果 60% 通过 给出2*n + 1 个的数字,除当中一个数字之外其它每一个数字均出现两次.找到这个数字. 您在真实的面试中是否遇到过这个题? Yes 例子 给出 [1, ...
- Effective C++ Item 10,11 Have assignment operators return a reference to *this Handle assignment to self in operator =
If you want to concatenate assignment like this int x, y, z; x = y = z = 15; The convention is to ma ...
- oracle数据库查询时间sql
select * from cc_picture_info where PICTURE_SOURCE = 3 AND UPLOAD_TIME > to_date('2017-03-29 16:5 ...