用Java实现的树

本文介绍了一种名为自由树的数据结构,其特点是元素不排序,允许重复的名称和值,通过UUID区分不同节点。支持深度优先和广度优先遍历,适用于分类树、目录树等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

琢磨其他东西的时候弄出来的副产品,自娱自乐用。

树的节点有名称和值两个公开的属性。每一个节点可以重名、重值,靠节点的内部UUID区分。因此,这个树类内部并不排序,是一种“乱序”树,因而也是“自由”树。

TreeNode类使用了Java的泛型技术,所以树节点的value可以是任何类型(但是同一棵树的所有节点的value只能是同种类型),name属性是字符串类型。

TreeNode的完整代码如下:

package generic;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import java.util.Vector;

/**
 * 实现数据结构——树的类。<br>
 * 特点:
 * <ul>
 * <li>纵向无限层级,横向无限扩展</li>
 * <li>元素不排序,不同元素的名称和值均可相同,靠UUID区分不同元素</li>
 * <li>支持将新节点插入到当前节点的子节点列表的任意位置</li>
 * <li>支持将一个树插入到另一棵树种,前提是两棵树的节点值具有相同的类型</li>
 * <li>采用泛型编程,所以节点可以是任意类型</li>
 * <li>可以用getLevel()方法获得节点在树中的层级</li>
 * <li>支持深度优先和广度优先两种遍历方法</li>
 * <li>适用于分类树、目录树、XML的DOM树等允许节点内容重复的情形</li>
 * </ul>
 *
 * 限制:
 * <ul>
 * <li>不直接支持插入孙节点及更下层的节点,要先找到子节点,再为该子节点插入子节点</li>
 * <li>由于元素不排序,所以搜索效率不高,不适用于大规模树</li>
 * <li>未考虑线程安全问题,所以在多线程情况下慎用</li>
 * <li>树中所有节点的值必须是同一类型</li>
 * </ul>
 *
 * @author kingfox
 * @version 1.00
 * @param <T> 指定树元素的类型
 */
public class TreeNode<T>
{
   /** 树节点的内部id */
   protected UUID uuid;
   /** 树节点的层级 */
   protected int level;
   /** 树节点的名字 */
   public String name;
   /** 树节点的值 */
   public T value;
   /** 树的父节点。对于树的根节点来说,该属性为null。 */
   protected TreeNode<T> parent;
   /** 树节点的子节点。 */
   protected List<TreeNode<T>> children;

   /**
    * 构造函数:构造树节点,其父节点默认为null。
    * @param name 指定树节点的名称
    * @param value 指定树节点的值
    */
   public TreeNode(String name, T value)
   {
      super();
      initNode(name, value, null);
   }

   /**
    * 构造函数:构造树节点,并挂接到某个父节点下。
    * @param name 树节点的名称
    * @param value 树节点的值
    * @param parent 指定父节点
    */
   public TreeNode(String name, T value, TreeNode<T> parent)
   {
      super();
      initNode(name, value, parent);
   }

   protected void initNode(String name, T value, TreeNode<T> parent)
   {
      this.name = name;
      this.value = value;
      this.parent = parent;
      if (parent == null)
      {
         level = 0;
      }
      else
      {
         level = parent.level + 1;
      }
      this.children = new Vector<>();
      this.uuid = UUID.randomUUID();
   }

   /**
    * 获取节点的ID。
    * @return 返回节点的UUID。
    */
   public UUID getUuid()
   {
      return uuid;
   }

   /**
    * 获取该节点在树中的层级
    * @return 返回节点在树中的层级
    */
   public int getLevel()
   {
      return level;
   }

   /**
    * 获取节点的父节点。
    * @return 返回节点的父节点。
    */
   public TreeNode<T> getParent()
   {
      return parent;
   }

   /**
    * 找到树的根节点。
    * @return 返回树的根节点。
    */
   public TreeNode<T> getRoot()
   {
      TreeNode<T> root = this;
      while(root.parent != null)
      {
         root = root.parent;
      }
      return root;
   }

   /**
    * 获取树节点的兄弟节点。
    * @return 返回该节点的所有兄弟节点。
    */
   public List<TreeNode<T>> getNeighbours()
   {
      return (parent != null) ? parent.children : null;
   }

   /**
    * 获取节点的子节点列表。
    * @return 返回节点的子节点列表。
    */
   public List<TreeNode<T>> getChildren()
   {
      return children;
   }

   /**
    * 新增一个子节点。
    * @param child 子节点对象。
    */
   public void addChild(TreeNode<T> child)
   {
      if (findNode(child.uuid, getRoot()) == null)   // 被插入节点在当前树中不存在时才会将该节点插入到树中。
      {
         child.parent = this;
         child.level = level + 1;
         children.add(child);
      }
   }

   /**
    * 将子节点插入到指定位置。
    * @param index 指定新增子节点在已有子节点中的位置。
    * @param child 新增的子节点对象。
    */
   public void addChild(int index, TreeNode<T> child)
   {
      if (findNode(child.uuid, getRoot()) != null)
      {
         child.parent = this;
         child.level = level + 1;
         children.add(index, child);
      }
   }

   /**
    * 新增一个子节点
    * @param name 新增子节点的名称
    * @param value 新增子节点的值
    * @return 返回新增的子节点
    */
   public TreeNode<T> addChild(String name, T value)
   {
      TreeNode<T> child = new TreeNode<T>(name, value);
      child.parent = this;
      child.level = level + 1;
      children.add(child);
      return child;
   }

   /**
    * 将子节点插入到指定位置。
    * @param index 指定新增子节点在已有子节点中的位置。
    * @param name 新增子节点的名称
    * @param value 新增子节点的值
    * @return 返回新增的子节点对象
    */
   public TreeNode<T> addChild(int index, String name, T value)
   {
      TreeNode<T> child = new TreeNode<T>(name, value);
      child.parent = this;
      child.level = level + 1;
      children.add(index, child);
      return child;
   }

   /**
    * 删除由uuid指定的子节点及其子节点。
    * @param uuid 指定待删除节点的uuid。
    * @return 若删除成功,则返回1;若uuid与根节点的uuid相同,则返回-1;若找不到指定的节点,则返回0。
    */
   public int deleteChild(UUID uuid)
   {
      int deleteCount = 0;
      if (this.uuid.equals(uuid) && (parent == null))
      {
         deleteCount = DCN_CANNOT_DELETE_ROOT;
      }
      else
      {
         TreeNode<T> node = findNode(uuid, parent);
         if (node != null)
         {
            node.deleteChildren();
            node.parent.children.remove(node);
            deleteCount = 1;
         }
         else
         {
            deleteCount = 0;
         }
      }
      return deleteCount;
   }

   /**
    * 删除指定名称的节点及其子节点。
    * @param name 指定待删除的节点的名称。
    * @return 若删除成功,则返回删除的节点数(不含其子节点);若节点名称与根节点相同,则返回-1;若找不到指定的节点,则返回0。
    */
   public int deleteChild(String name)
   {
      int deleteCount = 0;
      if (this.name.equals(name) && (parent == null))
      {
         deleteCount = DCN_CANNOT_DELETE_ROOT;
      }
      else
      {
         Object[] nodesArray = findNode(name, parent).toArray();
         deleteCount = nodesArray.length;
         for(int index = 0; index < deleteCount; index ++)
         {
            @SuppressWarnings("unchecked")
            TreeNode<T> node = (TreeNode<T>)nodesArray[deleteCount - index - 1];    // 反向迭代:要先删除下层节点,再删除上层节点。
            node.deleteChildren();     // 先删除该节点的所有子节点。
            node.parent.children.remove(node);     // 删除节点自身。
         }
      }
      return deleteCount;
   }

   /**
    * 删除所有子节点。
    */
   public void deleteChildren()
   {
      for(TreeNode<T> child : children)
      {
         child.deleteChildren();
      }
      children.clear();
   }

   /**
    * 根据节点的名称找到该节点。
    * @param name 节点的名称
    * @param from 从哪个节点开始搜寻。若from为null,则从树的根节点开始搜寻。
    * @return 具有相同名称的所有节点。
    */
   public List<TreeNode<T>> findNode(String name, TreeNode<T> from)
   {
      List<TreeNode<T>> nodes = new Vector<>();
      TreeNode<T> currNode = (from != null) ? from : this;
      // System.out.println(currNode);    __used_for_debug__

      if (currNode.name.equals(name))     // 判断当前节点是否匹配
      {
         nodes.add(currNode);
      }
      // 在所有子节点中查找匹配的节点
      for(TreeNode<T> child : currNode.children)
      {
         List<TreeNode<T>> matchedChildNodes = findNode(name, child);
         if (matchedChildNodes != null)
         {  // 若找到匹配的子节点(可能是多个),则这些匹配节点加入到节点列表中。
            nodes.addAll(matchedChildNodes);
         }
      }

      return (nodes.size() > 0) ? nodes : null;
   }

   /**
    * 根据节点的ID找到节点。
    * @param uuid 节点的uid
    * @param from 从哪个节点开始搜寻。若from为null,则从树的根节点开始搜寻。
    * @return uuid代表的节点。
    */
   public TreeNode<T> findNode(UUID uuid, TreeNode<T> from)
   {
      TreeNode<T> node = null;
      TreeNode<T> currNode = (from != null) ? from : this;
      // System.out.println(currNode);    __used_for_debug__

      if (currNode.uuid.equals(uuid))
      {
         node = currNode;
      }
      else
      {
         List<TreeNode<T>> children = currNode.children;
         for(TreeNode<T> child : children)
         {
            node = findNode(uuid, child);
            if (node != null)
            {
               break;
            }
         }
      }
      return node;
   }

   /**
    * 按深度优先遍历整颗树,按遍历顺序将树节点依次存放在列表中。
    * @return 返回存放了深度优先遍历结果的树节点列表。
    */
   public List<TreeNode<T>> walkByDepth()
   {
      List<TreeNode<T>> nodesList = new Vector<>();
      nodesList.add(this);    // 首先将自身加入到列表中。
      for(TreeNode<T> child : children)   // 对每一个子节点做深度优先遍历。
      {
         List<TreeNode<T>> childNodes = child.walkByDepth();
         nodesList.addAll(childNodes);
      }
      return nodesList;
   }

   /**
    * 按广度优先遍历整颗树,按遍历顺序将树节点依次存放在列表中。
    * @return 返回存放了广度优先遍历结果的树节点列表。
    */
   public List<TreeNode<T>> walkByBreadth()
   {
      List<TreeNode<T>> nodesList = new Vector<>();
      Queue<TreeNode<T>> relayQueue = new LinkedList<>();
      relayQueue.add(this);
      while(!relayQueue.isEmpty())
      {
         TreeNode<T> node = relayQueue.poll();
         nodesList.add(node);
         relayQueue.addAll(node.children);
      }
      return nodesList;
   }

   /**
    * 将树节点转换为字符串表达。
    */
   public String toString()
   {
      return "level: " + level + ", name: " + name + ", value: " + value + ", uuid: " + uuid.toString();
   }

   static final public int DCN_CANNOT_DELETE_ROOT = -1;

   /**
    * 该方法仅用于测试。
    * @param args 命令行参数
    */
   public static void main(String args[])
   {
      /* create the tree. */
      TreeNode<String> root = new TreeNode<String>("root", "0");

      TreeNode<String> child = new TreeNode<String>("child-01", "1");
      root.addChild(child);
      child.addChild(new TreeNode<String>("child-01-01", "1-1"));
      child.addChild(new TreeNode<String>("child-01-02", "1-2"));
      child.addChild(new TreeNode<String>("child-01-03", "1-3"));

      child = new TreeNode<String>("child-02", "2");
      root.addChild(child);
      child.addChild(new TreeNode<String>("child-02-01", "2-1"));
      child.addChild(new TreeNode<String>("child-03-01", "2-2"));

      child = new TreeNode<String>("child-03", "3");
      root.addChild(child);
      child.addChild(new TreeNode<String>("child-03-01", "3-1"));
      TreeNode<String> grandson = new TreeNode<String>("child-03-02", "3-2");
      child.addChild(grandson);
      child.addChild("child-03-01", "3-3");
      child.addChild("child-03-04", "3-4").addChild("child-03-04-01", "3-4-1");
      child.addChild("child-03-05", "3-5").addChild("child-03-01", "3-5-1");

      /* walk */
      List<TreeNode<String>> allNodes = root.walkByDepth();
      System.out.println("-----------------walk by depth-----------------");
      for(TreeNode<String> node : allNodes)
      {
         System.out.println(node);
      }
      allNodes = root.walkByBreadth();
      System.out.println("-----------------walk by breadth-----------------");
      for(TreeNode<String> node : allNodes)
      {
         System.out.println(node);
      }

      /* find all neighbours. */
      System.out.println("-----------get all neighbours-----------");
      List<TreeNode<String>> neibs = child.getNeighbours();
      for(TreeNode<String> neib : neibs)
      {
         System.out.println(neib);
      }

      /* find the soecified node by uuid */
      System.out.println("-----------find matched node by uuid-----------");
      TreeNode<String> nodeByUuid = root.findNode(grandson.uuid, null);
      if (nodeByUuid != null)
      {
         System.out.println("found: " + nodeByUuid);
      }
      else
      {
         System.out.println("not found: " + grandson.uuid);
      }
      nodeByUuid = root.findNode(UUID.randomUUID(), null);      // 随便弄个uuid,看看是否能匹配,结果自然是找不到。
      if (nodeByUuid != null)
      {
         System.out.println("found: " + nodeByUuid);
      }
      else
      {
         System.out.println("not found: " + child.uuid);
      }

      /* find the specified node by name */
      System.out.println("-----------find matched node by name-----------");
      List<TreeNode<String>> nodesByName = root.findNode("child-03-01", null);
      for(TreeNode<String> node : nodesByName)
      {
         System.out.println(node);
      }

      /* find root */
      System.out.println("------------------find root node---------------");
      TreeNode<String> rootNode = nodesByName.get(0).getRoot();
      System.out.println(rootNode);

      /* delete node by name */
      System.out.println("------------delete node by name----------------");
      int deleteCount = root.deleteChild("child-03-01");
      System.out.println("delete count of 'child-03-01': " + deleteCount);
      nodesByName = root.findNode("child-03-01", null);
      System.out.println("count of 'child-03-01': " + ((nodesByName == null) ? 0 : nodesByName.size()));

      /* delete node by uuid */
      System.out.println("-------------delete node by uuid---------------");
      UUID grandsonUuid = grandson.uuid;
      deleteCount = root.deleteChild(grandsonUuid);
      System.out.println("delete count of " + grandsonUuid + ": " + deleteCount);
      nodeByUuid = root.findNode(grandsonUuid, root);
      System.out.println("count of " + grandsonUuid + ": " + ((nodeByUuid == null) ? 0 : 1));

      /* delete all children */
      System.out.println("------------delete all children----------------");
      root.deleteChildren();
      System.out.println("count of children: " + root.children.size());
   }
}

 特点:

  • 纵向无限层级,横向无限扩展
  • 元素不排序,不同元素的名称和值均可相同,靠UUID区分不同元素
  • 支持将新节点插入到当前节点的子节点列表的任意位置
  • 支持将一个树插入到另一棵树种,前提是两棵树的节点值具有相同的类型
  • 采用泛型编程,所以节点可以是任意类型
  • 可以用getLevel()方法获得节点在树中的层级
  • 支持深度优先和广度优先两种遍历方法
  • 适用于分类树、目录树、XML的DOM树等允许节点内容重复的情形

限制:

  • 不直接支持插入孙节点及更下层的节点,要先找到子节点,再为该子节点插入子节点
  • 由于元素不排序,所以搜索效率不高,不适用于大规模树
  • 未考虑线程安全问题,所以在多线程情况下慎用
  • 树中所有节点的值必须是同一类型

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值