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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JAVA 实现二叉树(链式存储结构)

    二叉树的分类(按存储结构) 树的分类(按存储结构) 顺序存储(用数组表示(静态二叉树))   链式存储 一些特别的二叉根: 完全二叉树,平衡二叉树(AVL),线索二叉树,三叉的(带父亲的指针)    二叉搜索树或者叫二叉 查找树(BST)  所用二叉树如下图所示: 二叉树的Java实现(链式存储结构) class TreeNode { private int key = 0; private String data = null; private boolean isVisted = false

  • Java中树的存储结构实现示例代码

    一.树 树与线性表.栈.队列等线性结构不同,树是一种非线性结构. 一棵树只有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树了,而是多棵树的集合,也被称为森林. 二.树的父节点表示法 树中除根节点之外每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父节点. package com.ietree.basic.datastructure.tree; import java.util.ArrayList; import ja

  • Java实现级联下拉结构的示例代码

    目录 前言 构建统一返回下拉结构 构建集合<对象>转下拉树工具类 构建List<Map>转下拉或下拉树的工具类 前言 在开发过程中,会遇到很多的实体需要将查出的数据处理为下拉或者级联下拉的结构,提供给前端进行展示. 在数据库查出的结构中,可能是集合<实体类>的结构,也有可能是List<Map>的结构. 在下拉或者级联下拉的节点数据中,有时候还需要动态的携带其他的参数,已便于前端对某些数据的显示 如区域的级联下拉树中,需要携带经纬度的区域–在选择的时候在地图展

  • Java用邻接表存储图的示例代码

    目录 一.点睛 1.无向图 2.无向图的链接表 3.说明 4.无向图 二.邻接表的数据结构 1.节点 2.邻接点 三.算法步骤 四.实现 五.测试 一.点睛 邻接表是图的一种链式存储方法,其数据结构包括两部分:节点和邻接点. 用邻接表可以表示无向图,有向图和网.在此用无向图进行说明. 1.无向图 2.无向图的链接表 3.说明 节点 a 的邻接点是节点 b.d,其邻接点的存储下标为1.3,按照头插法(逆序)将其放入节点 a 后面的单链表中. 节点 b 的邻接点是节点 a.c.d,其邻接点的存储下标

  • Java使用泛型实现栈结构的示例代码

    目录 使用泛型实现栈结构 1.题目 2.解题思路 3.代码详解 多学一个知识点 使用泛型实现栈结构 1.题目 泛型是JAVA重要的特性,使用泛型编程,可以使代码复用率提高. 实现:使用泛型实现栈结构 2.解题思路 创建一个泛型类:Stack. 定义3个方法,入栈的push方法,出栈的pop方法,还有判断栈是否为空的empty()方法. 在底层实现上,使用LinkedList作为容器. 泛型类是含有一个或多个类型参数的类.定义泛型类很简单,只需要在类的名称后面加上“<”和“>”,并在其中指明类型

  • java中BigDecimal和0比较的示例代码

    BigDecimal 和 0 比较大小 调用BigDecimal中的compareTo方法, 如: int i = bigDecimal.compareTo(BigDecimal.Zero); i=0:表示bigDecimal的值  等于 0 i=1:表示bigDecimal的值与 大于0 i=-1:表示bigDecimal的值与 小于 0 参考案例 BigDecimal num= new BigDecimal("18"); int i=num.compareTo(BigDecimal

  • Java栈之链式栈存储结构的实现代码

    Java栈之链式栈存储结构实现 一.链栈 采用单链表来保存栈中所有元素,这种链式结构的栈称为链栈. 二.栈的链式存储结构实现 package com.ietree.basic.datastructure.stack; /** * 链栈 * * Created by ietree * 2017/4/29 */ public class LinkStack<T> { // 定义一个内部类Node,Node实例代表链栈的节点 private class Node { // 保存节点的数据 priva

  • Java中token的存储和获取实例代码

    目录 1. 获取token的工具类 2. header存储token 2.1 前端存储token 2.2 访问携带token 2.3 后端获取token并进行验证(拦截器中进行验证) 3. URL中的属性值 4. Cookie 4.1 控制器代码 4.2 测试 向Cookie中插入key - value值!!! 总结 1. 获取token的工具类 问:为什么写工具类呢???答:因为我们不知道前端将token怎么存储的,所以我们可以通过调用Token工具类来获取token.Token工具类会检查h

  • Java 中的注解详解及示例代码

    在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代码执行. 注解可以做什么 Java中的注解通常扮演以下角色 编译器指令 构建时指令 运行时指令 其中 Java内置了三种编译器指令,本文后面部分会重点介绍 Java注解可以应用在构建时,即当你构建你的项目时.构建过程包括生成源码,编译源码,生成xml文件,打包编译的源码和文件到JAR包等.软件的构建通常使用诸如Apache Ant和Mav

  • Java中使用Jedis操作Redis的示例代码

    使用Java操作Redis需要jedis-2.1.0.jar,下载地址:jedis-2.1.0.jar 如果需要使用Redis连接池的话,还需commons-pool-1.5.4.jar,下载地址:commons-pool-1.5.4.jar package com.test; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.j

  • java中实现汉字按照拼音排序(示例代码)

    最近項目中需要對繁體字按拼音進行排序 复制代码 代码如下: public static void main(String[] args) { Comparator cmp = Collator.getInstance(java.util.Locale.CHINA); String[] arr = { "张三", "李四", "王五", "刘六" ,"周濤","戴笠","戴阿&q

随机推荐