Java中树的存储结构实现
一、树
树与线性表、栈、队列等线性结构不同,树是一种非线性结构。
一棵树只有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树了,而是多棵树的集合,也被称为森林。
二、树的父节点表示法
树中除根节点之外每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父节点。
- package com.ietree.basic.datastructure.tree;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by ietree
- * 2017/4/30
- */
- public class TreeParent<E> {
- public static class Node<T> {
- T data;
- // 保存其父节点的位置
- int parent;
- public Node() {
- }
- public Node(T data) {
- this.data = data;
- }
- public Node(T data, int parent) {
- this.data = data;
- this.parent = parent;
- }
- public String toString() {
- return "TreeParent$Node[data=" + data + ", parent=" + parent + "]";
- }
- }
- private final int DEFAULT_TREE_SIZE = 100;
- private int treeSize = 0;
- // 使用一个Node[]数组来记录该树里的所有节点
- private Node<E>[] nodes;
- // 记录树的节点数
- private int nodeNums;
- // 以指定节点创建树
- public TreeParent(E data) {
- treeSize = DEFAULT_TREE_SIZE;
- nodes = new Node[treeSize];
- nodes[0] = new Node<E>(data, -1);
- nodeNums++;
- }
- // 以指定根节点、指定treeSize创建树
- public TreeParent(E data, int treeSize) {
- this.treeSize = treeSize;
- nodes = new Node[treeSize];
- nodes[0] = new Node<E>(data, -1);
- nodeNums++;
- }
- // 为指定节点添加子节点
- public void addNode(E data, Node parent) {
- for (int i = 0; i < treeSize; i++) {
- // 找到数组中第一个为null的元素,该元素保存新节点
- if (nodes[i] == null) {
- // 创建新节点,并用指定的数组元素保存它
- nodes[i] = new Node(data, pos(parent));
- nodeNums++;
- return;
- }
- }
- throw new RuntimeException("该树已满,无法添加新节点");
- }
- // 判断树是否为空
- public boolean empty() {
- // 根结点是否为null
- return nodes[0] == null;
- }
- // 返回根节点
- public Node<E> root() {
- // 返回根节点
- return nodes[0];
- }
- // 返回指定节点(非根结点)的父节点
- public Node<E> parent(Node node) {
- // 每个节点的parent记录了其父节点的位置
- return nodes[node.parent];
- }
- // 返回指定节点(非叶子节点)的所有子节点
- public List<Node<E>> children(Node parent) {
- List<Node<E>> list = new ArrayList<Node<E>>();
- for (int i = 0; i < treeSize; i++) {
- // 如果当前节点的父节点的位置等于parent节点的位置
- if (nodes[i] != null && nodes[i].parent == pos(parent)) {
- list.add(nodes[i]);
- }
- }
- return list;
- }
- // 返回该树的深度
- public int deep() {
- // 用于记录节点的最大深度
- int max = 0;
- for (int i = 0; i < treeSize && nodes[i] != null; i++) {
- // 初始化本节点的深度
- int def = 1;
- // m 记录当前节点的父节点的位置
- int m = nodes[i].parent;
- // 如果其父节点存在
- while (m != -1 && nodes[m] != null) {
- // 向上继续搜索父节点
- m = nodes[m].parent;
- def++;
- }
- if (max < def) {
- max = def;
- }
- }
- return max;
- }
- // 返回包含指定值的节点
- public int pos(Node node) {
- for (int i = 0; i < treeSize; i++) {
- // 找到指定节点
- if (nodes[i] == node) {
- return i;
- }
- }
- return -1;
- }
- }
测试类:
- package com.ietree.basic.datastructure.tree;
- import java.util.List;
- /**
- * Created by ietree
- * 2017/4/30
- */
- public class treeParentTest {
- public static void main(String[] args) {
- TreeParent<String> tp = new TreeParent<String>("root");
- TreeParent.Node root = tp.root();
- System.out.println(root);
- tp.addNode("节点1", root);
- System.out.println("此树的深度:" + tp.deep());
- tp.addNode("节点2", root);
- // 获取根节点的所有子节点
- List<TreeParent.Node<String>> nodes = tp.children(root);
- System.out.println("根节点的第一个子节点:" + nodes.get(0));
- // 为根节点的第一个子节点新增一个子节点
- tp.addNode("节点3", nodes.get(0));
- System.out.println("此树的深度:" + tp.deep());
- }
- }
程序输出:
- TreeParent$Node[data=root, parent=-1]
- 此树的深度:2
- 根节点的第一个子节点:TreeParent$Node[data=节点1, parent=0]
- 此树的深度:3
三、子节点链表示法
让父节点记住它的所有子节点。
- package com.ietree.basic.datastructure.tree;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by ietree
- * 2017/4/30
- */
- public class TreeChild<E> {
- private static class SonNode {
- // 记录当前节点的位置
- private int pos;
- private SonNode next;
- public SonNode(int pos, SonNode next) {
- this.pos = pos;
- this.next = next;
- }
- }
- public static class Node<T> {
- T data;
- // 记录第一个子节点
- SonNode first;
- public Node(T data) {
- this.data = data;
- this.first = null;
- }
- public String toString() {
- if (first != null) {
- return "TreeChild$Node[data=" + data + ", first=" + first.pos + "]";
- } else {
- return "TreeChild$Node[data=" + data + ", first=-1]";
- }
- }
- }
- private final int DEFAULT_TREE_SIZE = 100;
- private int treeSize = 0;
- // 使用一个Node[]数组来记录该树里的所有节点
- private Node<E>[] nodes;
- // 记录节点数
- private int nodeNums;
- // 以指定根节点创建树
- public TreeChild(E data) {
- treeSize = DEFAULT_TREE_SIZE;
- nodes = new Node[treeSize];
- nodes[0] = new Node<E>(data);
- nodeNums++;
- }
- // 以指定根节点、指定treeSize创建树
- public TreeChild(E data, int treeSize) {
- this.treeSize = treeSize;
- nodes = new Node[treeSize];
- nodes[0] = new Node<E>(data);
- nodeNums++;
- }
- // 为指定节点添加子节点
- public void addNode(E data, Node parent) {
- for (int i = 0; i < treeSize; i++) {
- // 找到数组中第一个为null的元素,该元素保存新节点
- if (nodes[i] == null) {
- // 创建新节点,并用指定数组元素保存它
- nodes[i] = new Node(data);
- if (parent.first == null) {
- parent.first = new SonNode(i, null);
- } else {
- SonNode next = parent.first;
- while (next.next != null) {
- next = next.next;
- }
- next.next = new SonNode(i, null);
- }
- nodeNums++;
- return;
- }
- }
- throw new RuntimeException("该树已满,无法添加新节点");
- }
- // 判断树是否为空
- public boolean empty() {
- // 根结点是否为null
- return nodes[0] == null;
- }
- // 返回根节点
- public Node<E> root() {
- // 返回根节点
- return nodes[0];
- }
- // 返回指定节点(非叶子节点)的所有子节点
- public List<Node<E>> children(Node parent) {
- List<Node<E>> list = new ArrayList<Node<E>>();
- // 获取parent节点的第一个子节点
- SonNode next = parent.first;
- // 沿着孩子链不断搜索下一个孩子节点
- while (next != null) {
- // 添加孩子链中的节点
- list.add(nodes[next.pos]);
- next = next.next;
- }
- return list;
- }
- // 返回指定节点(非叶子节点)的第index个子节点
- public Node<E> child(Node parent, int index) {
- // 获取parent节点的第一个子节点
- SonNode next = parent.first;
- // 沿着孩子链不断搜索下一个孩子节点
- for (int i = 0; next != null; i++) {
- if (index == i) {
- return nodes[next.pos];
- }
- next = next.next;
- }
- return null;
- }
- // 返回该树的深度
- public int deep() {
- // 获取该树的深度
- return deep(root());
- }
- // 这是一个递归方法:每棵子树的深度为其所有子树的最大深度 + 1
- private int deep(Node node) {
- if (node.first == null) {
- return 1;
- } else {
- // 记录其所有子树的最大深度
- int max = 0;
- SonNode next = node.first;
- // 沿着孩子链不断搜索下一个孩子节点
- while (next != null) {
- // 获取以其子节点为根的子树的深度
- int tmp = deep(nodes[next.pos]);
- if (tmp > max) {
- max = tmp;
- }
- next = next.next;
- }
- // 最后,返回其所有子树的最大深度 + 1
- return max + 1;
- }
- }
- // 返回包含指定值得节点
- public int pos(Node node) {
- for (int i = 0; i < treeSize; i++) {
- // 找到指定节点
- if (nodes[i] == node) {
- return i;
- }
- }
- return -1;
- }
- }
测试类:
- package com.ietree.basic.datastructure.tree;
- import java.util.List;
- /**
- * Created by ietree
- * 2017/4/30
- */
- public class TreeChildTest {
- public static void main(String[] args) {
- TreeChild<String> tp = new TreeChild<String>("root");
- TreeChild.Node root = tp.root();
- System.out.println(root);
- tp.addNode("节点1", root);
- tp.addNode("节点2", root);
- tp.addNode("节点3", root);
- System.out.println("添加子节点后的根结点:" + root);
- System.out.println("此树的深度:" + tp.deep());
- // 获取根节点的所有子节点
- List<TreeChild.Node<String>> nodes = tp.children(root);
- System.out.println("根节点的第一个子节点:" + nodes.get(0));
- // 为根节点的第一个子节点新增一个子节点
- tp.addNode("节点4", nodes.get(0));
- System.out.println("此树第一个子节点:" + nodes.get(0));
- System.out.println("此树的深度:" + tp.deep());
- }
- }
程序输出:
- TreeChild$Node[data=root, first=-1]
- 添加子节点后的根结点:TreeChild$Node[data=root, first=1]
- 此树的深度:2
- 根节点的第一个子节点:TreeChild$Node[data=节点1, first=-1]
- 此树第一个子节点:TreeChild$Node[data=节点1, first=4]
- 此树的深度:3
Java中树的存储结构实现的更多相关文章
- 原码,补码,反码的概念及Java中使用那种存储方式
原码,补码,反码的概念及Java中使用那种存储方式: 原码:原码表示法是机器数的一种简单的表示法.其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示 补码:机器数的补码可由原码得到.如果机器 ...
- JavaSE&&JavaEE&&JavaME的区别【Java中常用的包结构】
一.javaEEJavaSEJavaME用的同一个jar包吗? javaEE JavaSE javaME 用的JDK是同一个,开发j2SE工程的话只要有JDK就可以了,开发J2EE工程和J2ME工程除 ...
- Java栈之链式栈存储结构实现
一.链栈 采用单链表来保存栈中所有元素,这种链式结构的栈称为链栈. 二.栈的链式存储结构实现 package com.ietree.basic.datastructure.stack; /** * 链 ...
- JAVA中的数据存储(堆及堆栈)
转自:http://www.iteye.com/topic/6345301.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象 ...
- JAVA中的数据存储(堆及堆栈)- 转载
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可 ...
- Java杂谈4——Java中的字符串存储
Java中的String Java.Lang.String是Java语言自带的字符串实现,它并不是java的基本类型,但却和几乎每个java程序都密切相关的一个基础java类. string类内部实际 ...
- java面试一日一题:java中一个对象实例的结构是什么样子的
问题:请讲下在java程序运行时一个对象实例的数据结构是什么样子的 分析:该问题主要考察对java中对象的理解,在程序运行过程中一个对象实例是以什么样的形式存在的 回答要点: 主要从以下几点去考虑, ...
- 【转载】java 中变量的存储位置
原文链接点这里,感谢博主分享 * 寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. * 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出 ...
- java中变量的存储位置
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符 ...
随机推荐
- spark 系列文章汇总
源码导读 spark 源码导读1 从spark启动脚本开始 spark 源码导读2 进一步窥探Master.Worker启动及通信机制 spark 源码导读3 进一步理解脚本调用关系 spark 源码 ...
- HttpWatch工具简介及使用技巧(转)
HttpWatch是一个可用于录制HTTP请求信息的工具,由Simtec Limited公司开发,其官网为:Http://www.httpwatch.com,HttpWatch只支持IE和Firefo ...
- iOS TabBar添加阴影
效果图如下所示: 直接上代码 //移除顶部线条 self.tabBar.backgroundImage = [UIImage new]; self.tabBar.shadowImage = [UIIm ...
- java对象和json数据转换实现方式3-使用jackson实现
測试代码: package com.yanek.util.json; import java.io.IOException; import java.io.StringWriter; import j ...
- 一道money计算题引发的思考
网友提出一个问题如下 是小学和中学时候学到了增长折线问题,有点像数学问题,不过这个要求用编程来实现,恐怕还是有些逻辑要处理的,话不多说看代码吧 我给出的代码如下 代码清单: <?php func ...
- ISP (互联网服务提供商)
ISP(Internet Service Provider),互联网服务提供商,即向广大用户综合提供互联网接入业务.信息业务.和增值业务的电信运营商. ICP(Internet Content Pro ...
- Python--进阶处理4
#================第四章:迭代器和生成器=================== # 函数 itertools.islice()适用于在迭代器和生成器上做切片操作. import ite ...
- C#中命名空间别名的使用
C#中使用命名空间来分割不同的层级,在不同的层级中可以使用相同的类声明和变量声明.在程序中使用不同命名空间的下的相同名称的类时:可以用一下这几种方法进行限定: 1.使用完全限定名 using Syst ...
- Cookies Client Identification
HTTP The Definitive Guide Cookies are the best current way to identify users and allow persistent se ...
- Threaten Model
探测(扫描器,情报搜集)--入侵(vul,exp)--潜伏(RATS,setmft,AFSET)--横向入侵(admin cert,RATS)---信息泄漏(vpn,rats,通讯通道)--删除踪迹( ...