一、树

树与线性表、栈、队列等线性结构不同,树是一种非线性结构。

一棵树只有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树了,而是多棵树的集合,也被称为森林。

二、树的父节点表示法

树中除根节点之外每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个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中树的存储结构实现的更多相关文章

  1. 原码,补码,反码的概念及Java中使用那种存储方式

    原码,补码,反码的概念及Java中使用那种存储方式: 原码:原码表示法是机器数的一种简单的表示法.其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示 补码:机器数的补码可由原码得到.如果机器 ...

  2. JavaSE&&JavaEE&&JavaME的区别【Java中常用的包结构】

    一.javaEEJavaSEJavaME用的同一个jar包吗? javaEE JavaSE javaME 用的JDK是同一个,开发j2SE工程的话只要有JDK就可以了,开发J2EE工程和J2ME工程除 ...

  3. Java栈之链式栈存储结构实现

    一.链栈 采用单链表来保存栈中所有元素,这种链式结构的栈称为链栈. 二.栈的链式存储结构实现 package com.ietree.basic.datastructure.stack; /** * 链 ...

  4. JAVA中的数据存储(堆及堆栈)

    转自:http://www.iteye.com/topic/6345301.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象 ...

  5. JAVA中的数据存储(堆及堆栈)- 转载

    1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可 ...

  6. Java杂谈4——Java中的字符串存储

    Java中的String Java.Lang.String是Java语言自带的字符串实现,它并不是java的基本类型,但却和几乎每个java程序都密切相关的一个基础java类. string类内部实际 ...

  7. java面试一日一题:java中一个对象实例的结构是什么样子的

    问题:请讲下在java程序运行时一个对象实例的数据结构是什么样子的 分析:该问题主要考察对java中对象的理解,在程序运行过程中一个对象实例是以什么样的形式存在的 回答要点: 主要从以下几点去考虑, ...

  8. 【转载】java 中变量的存储位置

    原文链接点这里,感谢博主分享 * 寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. * 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出 ...

  9. java中变量的存储位置

    1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符 ...

随机推荐

  1. hdu 5065 数学题

    #include<iostream> #include<cmath> #include<cstdio> using namespace std; int A,B,y ...

  2. 【剑指offer】翻转单词顺序

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/27372033 题目描写叙述: JOBDU近期来了一个新员工Fish,每天早晨总是会拿着一本 ...

  3. SQLserver 设置自增为显式插入

    默认是状态是set IDENTITY_INSERT T2 off ,就是关闭了自动插入值的功能,为空时就会报错 ,,'') 报错: 消息 544,级别 16,状态 1,第 1 行当 IDENTITY_ ...

  4. Spring security UserDetailsService autowired注入失败错误

    最近使用spring mvc + spring security 实现登录权限控制的时候,一直不能成功登录,检查过后是dao一直无法注入为null CustomUserDetailConfig.jav ...

  5. 2015 DevOps状态调查报告

    依据Puppet Labs的2015 DevOps调查报告(下载地址:https://puppetlabs.com/2015-devops-report),调查研究表明: 拥有强IT服务绩效的企业一般 ...

  6. 编程之美 set 13 光影切割问题

    题目 给出几条线段, 求解这几条线段把给定平面切成的份数 思路 1. 枚举 3 条直线的情况, 发现有规律可循 两条直线, 一个交点 -> 空间分成 4 份 三条直线, 两个交点 -> 空 ...

  7. ArcGIS ArcMap 与 ArcServer关于Python的冲突

    一.问题描述 1.ArcMap 是32位,运行的Python也是32位: 2.ArcGIS Server 是64位,运行的Python是64位: 3.这样就导致注册表和环境变量起冲突,即如果Serve ...

  8. 通过AnimationSet 同步或一部播放多个动画 Android 属性动画(Property Animation) 完全解析 (下)

    AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等. 以下例子同时应用5个动画: 播放anim1: 同时播放anim2,anim3,a ...

  9. kindeditor在Java项目中的应用以及图片上传配置

    在官网下载Kindededitor的开发包   在项目中javaweb项目中导入kindeditor必须要使用的Jar包(用于文件上传,除非你的富文本编辑器不使用图片上传)jar包可以在官网的开发包中 ...

  10. jQuery设置内容和属性方

    何问起 hovertree.com 设置内容 - text().html() 以及 val()text() - 设置或返回所选元素的文本内容html() - 设置或返回所选元素的内容(包括 HTML ...