一、树

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

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

二、树的父节点表示法

树中除根节点之外每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父节点。

  1. package com.ietree.basic.datastructure.tree;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. /**
  7. * Created by ietree
  8. * 2017/4/30
  9. */
  10. public class TreeParent<E> {
  11.  
  12. public static class Node<T> {
  13.  
  14. T data;
  15. // 保存其父节点的位置
  16. int parent;
  17.  
  18. public Node() {
  19.  
  20. }
  21.  
  22. public Node(T data) {
  23. this.data = data;
  24. }
  25.  
  26. public Node(T data, int parent) {
  27. this.data = data;
  28. this.parent = parent;
  29. }
  30.  
  31. public String toString() {
  32. return "TreeParent$Node[data=" + data + ", parent=" + parent + "]";
  33. }
  34.  
  35. }
  36.  
  37. private final int DEFAULT_TREE_SIZE = 100;
  38. private int treeSize = 0;
  39. // 使用一个Node[]数组来记录该树里的所有节点
  40. private Node<E>[] nodes;
  41. // 记录树的节点数
  42. private int nodeNums;
  43.  
  44. // 以指定节点创建树
  45. public TreeParent(E data) {
  46. treeSize = DEFAULT_TREE_SIZE;
  47. nodes = new Node[treeSize];
  48. nodes[0] = new Node<E>(data, -1);
  49. nodeNums++;
  50. }
  51.  
  52. // 以指定根节点、指定treeSize创建树
  53. public TreeParent(E data, int treeSize) {
  54. this.treeSize = treeSize;
  55. nodes = new Node[treeSize];
  56. nodes[0] = new Node<E>(data, -1);
  57. nodeNums++;
  58. }
  59.  
  60. // 为指定节点添加子节点
  61. public void addNode(E data, Node parent) {
  62. for (int i = 0; i < treeSize; i++) {
  63. // 找到数组中第一个为null的元素,该元素保存新节点
  64. if (nodes[i] == null) {
  65. // 创建新节点,并用指定的数组元素保存它
  66. nodes[i] = new Node(data, pos(parent));
  67. nodeNums++;
  68. return;
  69. }
  70. }
  71. throw new RuntimeException("该树已满,无法添加新节点");
  72. }
  73.  
  74. // 判断树是否为空
  75. public boolean empty() {
  76. // 根结点是否为null
  77. return nodes[0] == null;
  78. }
  79.  
  80. // 返回根节点
  81. public Node<E> root() {
  82. // 返回根节点
  83. return nodes[0];
  84. }
  85.  
  86. // 返回指定节点(非根结点)的父节点
  87. public Node<E> parent(Node node) {
  88. // 每个节点的parent记录了其父节点的位置
  89. return nodes[node.parent];
  90. }
  91.  
  92. // 返回指定节点(非叶子节点)的所有子节点
  93. public List<Node<E>> children(Node parent) {
  94. List<Node<E>> list = new ArrayList<Node<E>>();
  95. for (int i = 0; i < treeSize; i++) {
  96. // 如果当前节点的父节点的位置等于parent节点的位置
  97. if (nodes[i] != null && nodes[i].parent == pos(parent)) {
  98. list.add(nodes[i]);
  99. }
  100. }
  101. return list;
  102. }
  103.  
  104. // 返回该树的深度
  105. public int deep() {
  106. // 用于记录节点的最大深度
  107. int max = 0;
  108. for (int i = 0; i < treeSize && nodes[i] != null; i++) {
  109. // 初始化本节点的深度
  110. int def = 1;
  111. // m 记录当前节点的父节点的位置
  112. int m = nodes[i].parent;
  113. // 如果其父节点存在
  114. while (m != -1 && nodes[m] != null) {
  115. // 向上继续搜索父节点
  116. m = nodes[m].parent;
  117. def++;
  118. }
  119. if (max < def) {
  120. max = def;
  121. }
  122. }
  123. return max;
  124. }
  125.  
  126. // 返回包含指定值的节点
  127. public int pos(Node node) {
  128. for (int i = 0; i < treeSize; i++) {
  129. // 找到指定节点
  130. if (nodes[i] == node) {
  131. return i;
  132. }
  133. }
  134. return -1;
  135. }
  136.  
  137. }

测试类:

  1. package com.ietree.basic.datastructure.tree;
  2.  
  3. import java.util.List;
  4.  
  5. /**
  6. * Created by ietree
  7. * 2017/4/30
  8. */
  9. public class treeParentTest {
  10.  
  11. public static void main(String[] args) {
  12.  
  13. TreeParent<String> tp = new TreeParent<String>("root");
  14. TreeParent.Node root = tp.root();
  15. System.out.println(root);
  16. tp.addNode("节点1", root);
  17. System.out.println("此树的深度:" + tp.deep());
  18. tp.addNode("节点2", root);
  19. // 获取根节点的所有子节点
  20. List<TreeParent.Node<String>> nodes = tp.children(root);
  21. System.out.println("根节点的第一个子节点:" + nodes.get(0));
  22. // 为根节点的第一个子节点新增一个子节点
  23. tp.addNode("节点3", nodes.get(0));
  24. System.out.println("此树的深度:" + tp.deep());
  25.  
  26. }
  27. }

程序输出:

  1. TreeParent$Node[data=root, parent=-1]
  2. 此树的深度:2
  3. 根节点的第一个子节点:TreeParent$Node[data=节点1, parent=0]
  4. 此树的深度:3

三、子节点链表示法

让父节点记住它的所有子节点。

  1. package com.ietree.basic.datastructure.tree;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. /**
  7. * Created by ietree
  8. * 2017/4/30
  9. */
  10. public class TreeChild<E> {
  11.  
  12. private static class SonNode {
  13. // 记录当前节点的位置
  14. private int pos;
  15. private SonNode next;
  16.  
  17. public SonNode(int pos, SonNode next) {
  18. this.pos = pos;
  19. this.next = next;
  20. }
  21. }
  22.  
  23. public static class Node<T> {
  24. T data;
  25. // 记录第一个子节点
  26. SonNode first;
  27.  
  28. public Node(T data) {
  29. this.data = data;
  30. this.first = null;
  31. }
  32.  
  33. public String toString() {
  34. if (first != null) {
  35. return "TreeChild$Node[data=" + data + ", first=" + first.pos + "]";
  36. } else {
  37. return "TreeChild$Node[data=" + data + ", first=-1]";
  38. }
  39. }
  40. }
  41.  
  42. private final int DEFAULT_TREE_SIZE = 100;
  43. private int treeSize = 0;
  44. // 使用一个Node[]数组来记录该树里的所有节点
  45. private Node<E>[] nodes;
  46. // 记录节点数
  47. private int nodeNums;
  48.  
  49. // 以指定根节点创建树
  50. public TreeChild(E data) {
  51. treeSize = DEFAULT_TREE_SIZE;
  52. nodes = new Node[treeSize];
  53. nodes[0] = new Node<E>(data);
  54. nodeNums++;
  55. }
  56.  
  57. // 以指定根节点、指定treeSize创建树
  58. public TreeChild(E data, int treeSize) {
  59. this.treeSize = treeSize;
  60. nodes = new Node[treeSize];
  61. nodes[0] = new Node<E>(data);
  62. nodeNums++;
  63. }
  64.  
  65. // 为指定节点添加子节点
  66. public void addNode(E data, Node parent) {
  67. for (int i = 0; i < treeSize; i++) {
  68. // 找到数组中第一个为null的元素,该元素保存新节点
  69. if (nodes[i] == null) {
  70. // 创建新节点,并用指定数组元素保存它
  71. nodes[i] = new Node(data);
  72. if (parent.first == null) {
  73. parent.first = new SonNode(i, null);
  74. } else {
  75. SonNode next = parent.first;
  76. while (next.next != null) {
  77. next = next.next;
  78. }
  79. next.next = new SonNode(i, null);
  80. }
  81. nodeNums++;
  82. return;
  83. }
  84. }
  85. throw new RuntimeException("该树已满,无法添加新节点");
  86. }
  87.  
  88. // 判断树是否为空
  89. public boolean empty() {
  90. // 根结点是否为null
  91. return nodes[0] == null;
  92. }
  93.  
  94. // 返回根节点
  95. public Node<E> root() {
  96. // 返回根节点
  97. return nodes[0];
  98. }
  99.  
  100. // 返回指定节点(非叶子节点)的所有子节点
  101. public List<Node<E>> children(Node parent) {
  102.  
  103. List<Node<E>> list = new ArrayList<Node<E>>();
  104. // 获取parent节点的第一个子节点
  105. SonNode next = parent.first;
  106. // 沿着孩子链不断搜索下一个孩子节点
  107. while (next != null) {
  108. // 添加孩子链中的节点
  109. list.add(nodes[next.pos]);
  110. next = next.next;
  111. }
  112. return list;
  113.  
  114. }
  115.  
  116. // 返回指定节点(非叶子节点)的第index个子节点
  117. public Node<E> child(Node parent, int index) {
  118. // 获取parent节点的第一个子节点
  119. SonNode next = parent.first;
  120. // 沿着孩子链不断搜索下一个孩子节点
  121. for (int i = 0; next != null; i++) {
  122. if (index == i) {
  123. return nodes[next.pos];
  124. }
  125. next = next.next;
  126. }
  127. return null;
  128. }
  129.  
  130. // 返回该树的深度
  131. public int deep() {
  132. // 获取该树的深度
  133. return deep(root());
  134. }
  135.  
  136. // 这是一个递归方法:每棵子树的深度为其所有子树的最大深度 + 1
  137. private int deep(Node node) {
  138. if (node.first == null) {
  139. return 1;
  140. } else {
  141. // 记录其所有子树的最大深度
  142. int max = 0;
  143. SonNode next = node.first;
  144. // 沿着孩子链不断搜索下一个孩子节点
  145. while (next != null) {
  146. // 获取以其子节点为根的子树的深度
  147. int tmp = deep(nodes[next.pos]);
  148. if (tmp > max) {
  149. max = tmp;
  150. }
  151. next = next.next;
  152. }
  153. // 最后,返回其所有子树的最大深度 + 1
  154. return max + 1;
  155. }
  156. }
  157.  
  158. // 返回包含指定值得节点
  159. public int pos(Node node) {
  160. for (int i = 0; i < treeSize; i++) {
  161. // 找到指定节点
  162. if (nodes[i] == node) {
  163. return i;
  164. }
  165. }
  166. return -1;
  167. }
  168.  
  169. }

测试类:

  1. package com.ietree.basic.datastructure.tree;
  2.  
  3. import java.util.List;
  4.  
  5. /**
  6. * Created by ietree
  7. * 2017/4/30
  8. */
  9. public class TreeChildTest {
  10.  
  11. public static void main(String[] args) {
  12.  
  13. TreeChild<String> tp = new TreeChild<String>("root");
  14. TreeChild.Node root = tp.root();
  15. System.out.println(root);
  16. tp.addNode("节点1", root);
  17. tp.addNode("节点2", root);
  18. tp.addNode("节点3", root);
  19. System.out.println("添加子节点后的根结点:" + root);
  20. System.out.println("此树的深度:" + tp.deep());
  21. // 获取根节点的所有子节点
  22. List<TreeChild.Node<String>> nodes = tp.children(root);
  23. System.out.println("根节点的第一个子节点:" + nodes.get(0));
  24. // 为根节点的第一个子节点新增一个子节点
  25. tp.addNode("节点4", nodes.get(0));
  26. System.out.println("此树第一个子节点:" + nodes.get(0));
  27. System.out.println("此树的深度:" + tp.deep());
  28.  
  29. }
  30.  
  31. }

程序输出:

  1. TreeChild$Node[data=root, first=-1]
  2. 添加子节点后的根结点:TreeChild$Node[data=root, first=1]
  3. 此树的深度:2
  4. 根节点的第一个子节点:TreeChild$Node[data=节点1, first=-1]
  5. 此树第一个子节点:TreeChild$Node[data=节点1, first=4]
  6. 此树的深度: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. spark 系列文章汇总

    源码导读 spark 源码导读1 从spark启动脚本开始 spark 源码导读2 进一步窥探Master.Worker启动及通信机制 spark 源码导读3 进一步理解脚本调用关系 spark 源码 ...

  2. HttpWatch工具简介及使用技巧(转)

    HttpWatch是一个可用于录制HTTP请求信息的工具,由Simtec Limited公司开发,其官网为:Http://www.httpwatch.com,HttpWatch只支持IE和Firefo ...

  3. iOS TabBar添加阴影

    效果图如下所示: 直接上代码 //移除顶部线条 self.tabBar.backgroundImage = [UIImage new]; self.tabBar.shadowImage = [UIIm ...

  4. java对象和json数据转换实现方式3-使用jackson实现

    測试代码: package com.yanek.util.json; import java.io.IOException; import java.io.StringWriter; import j ...

  5. 一道money计算题引发的思考

    网友提出一个问题如下 是小学和中学时候学到了增长折线问题,有点像数学问题,不过这个要求用编程来实现,恐怕还是有些逻辑要处理的,话不多说看代码吧 我给出的代码如下 代码清单: <?php func ...

  6. ISP (互联网服务提供商)

    ISP(Internet Service Provider),互联网服务提供商,即向广大用户综合提供互联网接入业务.信息业务.和增值业务的电信运营商. ICP(Internet Content Pro ...

  7. Python--进阶处理4

    #================第四章:迭代器和生成器=================== # 函数 itertools.islice()适用于在迭代器和生成器上做切片操作. import ite ...

  8. C#中命名空间别名的使用

    C#中使用命名空间来分割不同的层级,在不同的层级中可以使用相同的类声明和变量声明.在程序中使用不同命名空间的下的相同名称的类时:可以用一下这几种方法进行限定: 1.使用完全限定名 using Syst ...

  9. Cookies Client Identification

    HTTP The Definitive Guide Cookies are the best current way to identify users and allow persistent se ...

  10. Threaten Model

    探测(扫描器,情报搜集)--入侵(vul,exp)--潜伏(RATS,setmft,AFSET)--横向入侵(admin cert,RATS)---信息泄漏(vpn,rats,通讯通道)--删除踪迹( ...