Android 版数据结构与算法 (六): 树与二叉树

之前的篇章主要讲解了数据结构中的线性结构, 所谓线性结构就是数据与数据之间是一对一的关系, 接下来我们就要进入非线性结构的世界了, 主要是树与图, 好了接下来我们将会了解到树以及二叉树, 二叉平衡树, 赫夫曼树等原理以及 java 代码的实现, 先从最基础的开始学习吧.

一, 树

树的定义:

树是 n(n>=0) 个结点的有限集合.

当 n=0 时, 集合为空, 称为空树.

在任意一颗非空树中, 有且仅有一个特定的结点称为根.

当 n>1 时, 除根结点以外的其余结点可分成 m(m>=0) 个不相交的有限结点集合 T1,T2....Tm. 其中每个集合本身也是一棵树, 称为根的子树.

如下图就是一棵树:

ab7653affab982b574eb7acc55df2e04.gif

可以看到, 树这种数据结构数据之间是一对一或者一对多关系, 不再是一对一的关系

在上图中节点 A 叫做整棵树的根节点, 一棵树中只有一个根节点.

根节点可以生出多个孩子节点, 孩子节点又可以生出多个孩子节点. 比如 A 的孩子节点为 B 和 C,D 的孩子节点为 G,H,I.

每个孩子节点只有一个父节点, 比如 D 的父节点为 B,E 的父节点为 C.

好了, 关于树的定义介绍到这, 很简单.

二, 树的相关术语

ab7653affab982b574eb7acc55df2e04.gif

节点的度

节点含有的子树个数, 叫做节点的度. 度为 0 的节点成为叶子结点或终端结点. 比如上图中 D 的度为 3,E 的度为 1.

G,H,I,J 的度为 0, 叫做叶子结点.

树的度

一棵树中 最大节点的度树的度. 比如上图中树的度为 3

结点的层次

从根结点算起, 为第一层, 其余依次类推如上图. B,C 的层次为 2,G,H 的层次为 4.

树中节点的最大层次称为树的高度或深度. 上图中树的高度或深度为 4

三, 树的存储结构

简单的顺序存储不能满足树的实现, 需要结合顺序存储和链式存储来解决.

树的存储方式主要有三种:

双亲表示法: 每个节点不仅保存自己数据还附带一个指示器指示其父节点的角标, 这种方式可以用数组来存储.

如图:

ab7653affab982b574eb7acc55df2e04.gif

这种存储方式特点是: 查找一个节点的孩子节点会很麻烦但是查找其父节点很简单.

孩子表示法: 每个节点不仅保存自己数据信息还附带指示其孩子的指示器, 这种方式用链表来存储比较合适.

如图:

ab7653affab982b574eb7acc55df2e04.gif

这种存储方式特点是: 查找一个节点的父亲节点会很麻烦但是查找其孩子节点很简单.

理想表示法: 数组 + 链表的存储方式, 把每个结点的孩子结点排列起来, 以单链表方式连接起来, 则 n 个孩子有 n 个孩子链表, 如果是叶子结点则此链表为空, 然后 n 个头指针又组成线性表, 采用顺序存储方式, 存储在一个一维数组中.

如图:

ab7653affab982b574eb7acc55df2e04.gif

这种方式查找父节点与孩子结点都比较简便.

以上主要介绍了树的一些概念以及存储方式介绍, 实际我们用的更多的是二叉树, 接下来我们看下二叉树.

四, 二叉树的概念

二叉树定义: 二叉树是 n(n>=0) 个结点的有限集合, 该集合或者为空, 或者由一个根结点和两课互不相交的, 分别称为根结点左子树和右子树的二叉树组成.

用人话说, 二叉树是每个节点至多有两个子树的树.

如图就是一颗二叉树:

ab7653affab982b574eb7acc55df2e04.gif

五, 特殊二叉树

斜树: 所有结点只有左子树的二叉树叫做左斜树, 所有结点只有右子树的二叉树叫做右斜树.

如图:

ab7653affab982b574eb7acc55df2e04.gif

满二叉树: 在一棵二叉树中, 所有分支结点都有左子树与右子树, 并且所有叶子结点都在同一层则为满二叉树.

如图:

ab7653affab982b574eb7acc55df2e04.gif

完全二叉树: 所有叶子节点都出现在 k 或者 k-1 层, 而且从 1 到 k-1 层必须达到最大节点数, 第 k 层可是不是慢的, 但是第 k 层的所有节点必须集中在最左边.

如图:

ab7653affab982b574eb7acc55df2e04.gif

六, 二叉树的遍历

二叉树的遍历主要有三种: 先序遍历, 中序遍历, 后续遍历, 接下来我们挨个了解一下.

先序遍历: 先访问根结点, 再先序遍历左子树, 再先序遍历右子树.

如图所示:

ab7653affab982b574eb7acc55df2e04.gif

先序遍历结果为: ABDGHCEIF

中序遍历: 先中序遍历左子树, 再访问根结点, 再中序遍历右子树.

如图:

ab7653affab982b574eb7acc55df2e04.gif

中序遍历结果为: GDHBAEICF

后序遍历: 先后序遍历左子树, 再后序遍历右子树, 再访问根结点.

如图:

ab7653affab982b574eb7acc55df2e04.gif

后序遍历结果: GHDBIEFCA

七, java 实现二叉树

先来看看每个结点类:publicclassTreeNode{

privateStringdata;// 自己结点数据

privateTreeNodeleftChild;// 左孩子

privateTreeNoderightChild;// 右孩子

publicStringgetData(){

returndata;

}

publicvoidsetData(Stringdata){

this.data=data;

}

publicTreeNode(Stringdata){

this.data=data;

this.leftChild=null;

this.rightChild=null;

}

}

很简单, 每个结点信息包含自己结点数据以及指向左右孩子的指针 (为了方便, 我这里就叫指针了).

二叉树的创建

我们创建如下二叉树:

ab7653affab982b574eb7acc55df2e04.gif

代码实现:publicclassBinaryTree{

privateTreeNoderoot=null;

publicTreeNodegetRoot(){

returnroot;

}

publicBinaryTree(){

root=newTreeNode("A");

}

/**

* 构建二叉树

* A

* B C

* D E F G

*/

publicvoidcreateBinaryTree(){

TreeNodenodeB=newTreeNode("B");

TreeNodenodeC=newTreeNode("C");

TreeNodenodeD=newTreeNode("D");

TreeNodenodeE=newTreeNode("E");

TreeNodenodeF=newTreeNode("F");

TreeNodenodeG=newTreeNode("G");

root.leftChild=nodeB;

root.rightChild=nodeC;

nodeB.leftChild=nodeD;

nodeB.rightChild=nodeE;

nodeC.leftChild=nodeF;

nodeC.rightChild=nodeG;

}

.......

}

创建 BinaryTree 的时候就已经创建根结点 A,createBinaryTree() 方法中创建其余结点并且建立相应关系.

获得二叉树的高度

树中节点的最大层次称为树的高度, 因此获得树的高度需要递归获取所有节点的高度, 取最大值./**

* 求二叉树的高度

* @author Administrator

*

*/

publicintgetHeight(){

returngetHeight(root);

}

privateintgetHeight(TreeNodenode){

if(node==null){

return0;

}else{

inti=getHeight(node.leftChild);

intj=getHeight(node.rightChild);

return(i

}

}

获取二叉树的结点数

获取二叉树结点总数, 需要遍历左右子树然后相加/**

* 获取二叉树的结点数

* @author Administrator

*

*/

publicintgetSize(){

returngetSize(root);

}

privateintgetSize(TreeNodenode){

if(node==null){

return0;

}else{

return1+getSize(node.leftChild)+getSize(node.rightChild);

}

}

二叉树的遍历

二叉树遍历分为前序遍历, 中序遍历, 后续遍历, 主要也是递归思想, 下面直接给出代码/**

* 前序遍历 -- 迭代

* @author Administrator

*

*/

publicvoidpreOrder(TreeNodenode){

if(node==null){

return;

}else{

System.out.println("preOrder data:"+node.getData());

preOrder(node.leftChild);

preOrder(node.rightChild);

}

}

/**

* 中序遍历 -- 迭代

* @author Administrator

*

*/

publicvoidmidOrder(TreeNodenode){

if(node==null){

return;

}else{

midOrder(node.leftChild);

System.out.println("midOrder data:"+node.getData());

midOrder(node.rightChild);

}

}

/**

* 后序遍历 -- 迭代

* @author Administrator

*

*/

publicvoidpostOrder(TreeNodenode){

if(node==null){

return;

}else{

postOrder(node.leftChild);

postOrder(node.rightChild);

System.out.println("postOrder data:"+node.getData());

}

}

获取某一结点的父结点

获取结点的父节点也是递归思想, 先判断当前节点左右孩子是否与给定节点信息相等, 相等则当前结点即为给定结点的父节点, 否则继续递归左子树, 右子树./**

* 查找某一结点的父结点

* @param data

* @return

*/

publicTreeNodegetParent(Stringdata){

// 封装为内部结点信息

TreeNodenode=newTreeNode(data);

//

if(root==null||node.data.equals(root.data)){

// 根结点为 null 或者要查找的结点就为根结点, 则直接返回 null, 根结点没有父结点

returnnull;

}

returngetParent(root,node);// 递归查找

}

publicTreeNodegetParent(TreeNodesubTree,TreeNodenode){

if(null==subTree){// 子树为 null, 直接返回 null

returnnull;

}

// 判断左或者右结点是否与给定结点相等, 相等则此结点即为给定结点的父结点

if(subTree.leftChild.data.equals(node.data)||subTree.rightChild.data.equals(node.data)){

returnsubTree;

}

// 以上都不符合, 则递归查找

if(getParent(subTree.leftChild,node)!=null){// 先查找左子树, 左子树找不到查询右子树

returngetParent(subTree.leftChild,node);

}else{

returngetParent(subTree.rightChild,node);

}

}

八, 总结

以上总结了树与二叉树的一些概念, 重点就是二叉树的遍历以及 java 代码实现, 比较简单, 没什么多余解释, 下一篇了解一下赫夫曼树以及二叉排序树.

来源: https://www.cnblogs.com/leipDao/p/9707613.html

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐