一、树的基本知识

  树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树;

1、结点的层次和树的深度
  树的结点包含一个数据元素及若干指向其子树的若干分支。结点的层次( level) 从根开始定义,层次数为 0 的结点是根结点,其子树的根的层次数为 1。若结点在 L 层,其子树的根就在 L+1 层。对于层次为 k( k > 0)的每个结点 c,都有且仅有一个层次为 k-1 的结点 p与之对应, p 称为 c 的父亲( parent) 或父结点。若 p 是 c 的父亲,则 c 称为 p 的孩子( child)。父子之间的连线是树的一条边。在树中根结点没有父亲,其余结点只有一个父结点,但是却可能有多个孩子,同一结点的孩子相互称为兄弟( sibling)。树中结点的最大层次数称为树的深度( Depth) 或高度。树中结点也有高度,其高度是以该结点为根的树的高度。
2、结点的度与树的度
  结点拥有的子树的数目称为结点的度( Degree)。度为 0 的结点称为叶子( leaf) 或终端结点。度不为 0 的结点称为非终端结点或分支结点。除根之外的分支结点也称为内部结点。在这里需要注意的是结点的直接前驱结点,即它的父结点不计入其度数。
3、在树结构中有一个重要的性质如下:
  树中的结点数等于树的边数加 1,也等于所有结点的度数之和加 1。这是因为除根结点以外每个结点都与指向它的一条边对应,所以除根结点以外的结点数等于树中边数之和。因此树中的结点数等于树的边数加 1。而边数之和就是所有结点的度数之和,因此树中的结点数也等于所有结点的度数之和加 1。
  这个性质说明在树中结点总数与边的总数是相当的,基于这一事实,在对涉及树结构的算法复杂性进行分析时,可以用结点的数目作为规模的度量。
4、路径
  在树中k+1 个结点通过k条边连接构成的序列{( v0,v1) ,( v1,v2) , … ,( vk-1,vk) | k ≥ 0},称为长度为k的路径( path)。注意,此时忽略了树中边的方向。由单个结点, 0 条边构成的是长度为 0 的路径。结论:树中任意两个结点之间都存在唯一的路径。这意味着树既是连通的,同时又不会出现环路。从根结点开始,存在到其他任意结点的一条唯一路径,根到某个结点路径的长度,恰好是该结点的层次数。
5、祖先、子孙、堂兄弟
  将父子关系进行扩展,就可以得到祖先、子孙、堂兄弟等关系。结点的祖先是从根到该结点路径上的所有结点。以某结点为根的树中的任一结点都称为该结点的子孙。父亲在同一层次的结点互为堂兄弟。
6、有序树、m 叉树、森林
  如果将树中结点的各子树看成是从左至右是有次序的,则称该树为有序树;若不考虑子树的顺序则称为无序树。对于有序树,我们可以明确的定义每个结点的第一个孩子、第二个孩子等,直到最后一个孩子。若不特别指明,一般讨论的树都是有序树。
  树中所有结点最大度数为 m 的有序树称为 m 叉树。
  森林( forest) 是 m( m ≥ 0 )棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。树和森林的概念相近。删去一棵树的根,就得到一个森林;反之,加上一个结点作树根,森林就变为一棵树。
二、代码实现
1、树的结点类:

 import java.util.List;

 public class TreeNode<E> {
public E key;// data
public TreeNode<E> parent;// parent
public List<TreeNode<E>> children;// children public TreeNode(E key, TreeNode<E> parent) {
this.key = key;
this.parent = parent;
} public TreeNode(E key) {
this.key = key;
} @Override
public String toString() {
return "BSTNode [key=" + key + "]";
} }

2、树的接口类:

 import java.util.List;

 public interface ITree<E> {
/**
* 节点数
* @return
*/
int getSize(); /**
* 获取根节点
* @return
*/
TreeNode<E> getRoot(); /**
* 获取x的父节点
* @param x
* @return
*/
TreeNode<E> getParent(TreeNode<E> x); /**
* 获取第一个儿子
* @param x
* @return
*/
TreeNode<E> getFirstChild(TreeNode<E> x); /**
* 获取x的下一个兄弟
* @param x
* @return
*/
TreeNode<E> getNextSibling(TreeNode<E> x); /**
* 子树高度
* @param x
* @return
*/
int getHeight(TreeNode<E> x); /**
* 插入子节点
* @param x
* @param child
*/
void insertChild(TreeNode<E> x, TreeNode<E> child); /**
* 删除第i个子节点
* @param x
* @param i
*/
void deleteChild(TreeNode<E> x, int i); /**
* 先序遍历
* @param x
* @return
*/
List<TreeNode<E>> preOrder(TreeNode<E> x); /**
* 后续遍历
* @param x
* @return
*/
List<TreeNode<E>> postOrder(TreeNode<E> x); /**
* 层次遍历
* @param x
* @return
*/
List<List<TreeNode<E>>> levelOrder(TreeNode<E> x); List<List<TreeNode<E>>> levelOrder();
}

3、MyTree类:

  注意这儿层次遍历这个函数,用到的bfs(宽度优先搜索),顾名思义就是遍历完每一层再接着遍历下一层。相比于DFS用的递归而言,那么bfs用到的技巧就是队列,口诀:(队)弹一个,加N个(队)。如果某行有n个节点,从1访问到n时,需要保存1的左右节点,2的左右节点,,,n的左右节点,遍历下行时的顺序是1的左右,2的左右,,,n的左右。这不就是先进(先保存)先出(先访问)吗?算法伪码:1.root进队(每个元素都要进队,别直接访问)2.开始循环:队首出队,其左右节点进队。(当然你也可以左右节点先进队,接着队首出队) 循环条件是队非空。

  但若是需要换行打印的话就比较麻烦了,第一行的换行很简单啊,root后直接换行。那么你已经知道了第一行的换行,你就知道了第二行的换行位置啊,毕竟第二行都是第一行的“映射”。所以呢,跟刚才一样,在访问第n行的时候就要保存第n+1行的最右信息,反正n+1行的最右就是从第n行的左节点(第n+1行的最左)开始迭代。那么就需要用到双指针:增加两个TreeNode:last和nlast。last:表示当前遍历层最右结点。nlast:表示下一层最右结点。遍历时,每次将nlast指向插入队列元素,最后一个插入结点时即最右结点。插入左右孩子之后,检测last是否为当前输出结点,若是,则表示需要进行换行,并将last指向下一层的nlast。

 import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue; public class MyTree<E> implements ITree<E> {
private int size = 0;
private TreeNode root; public MyTree(TreeNode root) {
this.root = root;
size++;
} @Override
public int getSize() {
return size;
} @Override
public TreeNode<E> getRoot() {
return root;
} @Override
public TreeNode<E> getParent(TreeNode<E> x) {
return x.parent;
} @Override
public TreeNode<E> getFirstChild(TreeNode<E> x) {
return x.children.get(0);
} @Override
public TreeNode<E> getNextSibling(TreeNode<E> x) {
List<TreeNode<E>> children = x.parent.children;
int i = children.indexOf(x);
// try {
// return children.get(i + 1);
// } catch (Exception e) {
// return null;
// }
if (i == children.size() - 1) {
return null;
} else {
return children.get(i + 1);
}
} /**
* 获得整棵树的高度
*/
public int getHeight() {
return getHeight(root);
} // dfs 深度优先遍历求高度
@Override
public int getHeight(TreeNode<E> x) {
if (x.children == null) {
return 0;
} else {
int h = 0;
for (int i = 0; i < x.children.size(); i++) {
h = Math.max(h, getHeight(x.children.get(i)));
}
return h + 1;
}
} @Override
public void insertChild(TreeNode<E> p, TreeNode<E> child) {
if (p.children == null) {
p.children = new ArrayList<>();
}
p.children.add(child);
child.parent = p;
size++;
} @Override
public void deleteChild(TreeNode<E> p, int i) {
p.children.remove(i);
size--;
} // 因为这不是二叉树,所以先序等遍历就先不写
@Override
public List<TreeNode<E>> preOrder(TreeNode<E> x) {
return null;
} @Override
public List<TreeNode<E>> postOrder(TreeNode<E> x) {
return null;
} // 层次遍历 bfs 宽度优先搜索 队列 口诀:(队)弹一个,加N个(队)
// 深搜 递归
// 双指针 换行
@Override
public List<List<TreeNode<E>>> levelOrder(TreeNode<E> x) {
List<List<TreeNode<E>>> res = new ArrayList<>();// list的list
Queue<TreeNode<E>> q = new LinkedList<>();
q.add(x);// 初始化
TreeNode<E> last = x;// 标记上一行的最末节点
TreeNode<E> nLast = null;// 标记最新加入队列的节点
List<TreeNode<E>> l = new ArrayList<>();// 第一行的list
res.add(l); while (!q.isEmpty()) {
TreeNode<E> peek = q.peek();
// 把即将弹出的节点的子节点加入队列
if (peek.children != null) {
for (TreeNode<E> n : peek.children) {
q.add(n);
nLast = n;
}
}
l.add(q.poll());// 弹出,加入到当前层列表 if (peek == last && !q.isEmpty()) {// 如果现在弹出的节点是之前标记的最后节点,就要换列表
l = new ArrayList<>();
res.add(l);
last = nLast;
}
}
return res;
} @Override
public List<List<TreeNode<E>>> levelOrder() {
return levelOrder(root);
}
}
 

树的简介及Java实现的更多相关文章

  1. 数据结构图文解析之:树的简介及二叉排序树C++模板实现.

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  2. 相似文档查找算法之 simHash 简介及其 java 实现 - leejun_2005的个人页面 - 开源中国社区

    相似文档查找算法之 simHash 简介及其 java 实现 - leejun_2005的个人页面 - 开源中国社区 相似文档查找算法之 simHash 简介及其 java 实现

  3. simHash 简介以及java实现

    http://gemantic.iteye.com/blog/1701101 simHash 简介以及java实现 博客分类: 算法 simHash java 去重  传统的hash 算法只负责将原始 ...

  4. 深入浅出数据结构C语言版(10)——树的简介

    到目前为止,我们一直在谈论的数据结构都是"线性结构",不论是普通链表.栈还是队列,其中的每个元素(除了第一个和最后一个)都只有一个前驱(排在前面的元素)和一个后继(排在后面的元素) ...

  5. simHash 简介以及 java 实现

    传统的 hash 算法只负责将原始内容尽量均匀随机地映射为一个签名值,原理上相当于伪随机数产生算法.产生的两个签名,如果相等,说明原始内容在一定概 率 下是相等的:如果不相等,除了说明原始内容不相等外 ...

  6. Java密码体系结构简介:Java Cryptography Architecture (JCA) Reference Guide

    来自Java官方的文档,作备忘使用. 简介: Java平台非常强调安全性,包括语言安全,密码学,公钥基础设施,认证,安全通信和访问控制. JCA是平台的一个主要部分,包含一个“提供者”体系结构和一组用 ...

  7. Elasticsearch Java client(ES Client 简介、Java REST Client、Java Client、Spring Data Elasticsearch)

    elasticsearch系列七:ES Java客户端-Elasticsearch Java client(ES Client 简介.Java REST Client.Java Client.Spri ...

  8. 标准Trie字典树学习二:Java实现方式之一

    特别声明: 博文主要是学习过程中的知识整理,以便之后的查阅回顾.部分内容来源于网络(如有摘录未标注请指出).内容如有差错,也欢迎指正! 系列文章: 1. 标准Trie字典树学习一:原理解析 2.标准T ...

  9. 数字证书简介及Java编码实现

    1.数字证书简介 数字证书具备常规加密解密必要的信息,包含签名算法,可用于网络数据加密解密交互,标识网络用户(计算机)身份.数字证书为发布公钥提供了一种简便的途径,其数字证书则成为加密算法以及公钥的载 ...

随机推荐

  1. 动态的加载显示oracle警告日志文件内容

    Last login: Fri Jan 25 00:37:47 2019 from oracle [root@oracle ~]# su - oracle [oracle@oracle ~]$ sql ...

  2. C语言的数组指针

    数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element).数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存.以int arr[] = { 99, ...

  3. ISP基本框架及算法介绍

    什么是ISP,他的工作原理是怎样的? ISP是Image Signal Processor的缩写,全称是影像处理器.在相机成像的整个环节中,它负责接收感光元件(Sensor)的原始信号数据,可以理解为 ...

  4. windows 下 nginx log 分割

    默认 nginx 不支持 log自动分割     windows下 解决方案:    1.首先创建bat脚本 split_log.bat , 并保存在nginx 目录下: @echo off rem ...

  5. datatable拆分多个

    /// <summary> /// 分解数据表 /// </summary> /// <param name="originalTab">需要分 ...

  6. zabbix企业级监控dell硬件服务状态

    监控dell服务的硬件状态,一般有两种方式 第一,在操作熊上安装OMSA,编写脚本调用omreport命令进行监控 第二.使用IDRAC,只需开启在IARAC上的SNMP,zabbix通过snmp进行 ...

  7. UOJ#450. 【集训队作业2018】复读机 排列组合 生成函数 单位根反演

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ450.html 题解 首先有一个东西叫做“单位根反演”,它在 FFT 的时候用到过: $$\frac 1 ...

  8. 谷歌浏览器F12基本用法

    第一步:打开你想进行调试的页面,并按F12进入到调试模式 此处以百度页面为例进行功能展示 这是关于最右侧“元素选择器”的功能展示 关于第二个功能的使用,这个功能是将页面适应成手机屏幕大小, eleme ...

  9. 硬核机器学习干货,手把手教你写KNN!

    机器学习相关概念 人工智能.机器学习和深度学习的关系 在探讨算法之前,我们先来谈一谈什么是机器学习.相信大家都听说过AlphaGo:2016年3月,AlphaGo与围棋世界冠军李世石进行围棋人机大战, ...

  10. js/jQuery使用过程中常见问题/已踩过的坑大杂烩

    目录 一.jQuery选择器选择选中的或者disabled的选择框时attr函数无效 二.jQuery each函数的break/continue 三.jQuery 获取元素的left会值/left数 ...