存储结构二叉树

存储结构二叉树

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

二叉树的存储结构有两种:顺序存储结构和链式存储结构。
顺序存储结构
对于满二叉树和全然二叉树来说,能够将其数据元素逐层存放到一组连续的存储单元中,如图6-3 所看到的。

用一维数组来实现顺序存储结构时。将二叉树中编号为i 的结点存放到数组中的第i 个分量中。如此依据性质6.7,能够得到结点i 的父结点、左右孩子结点分别存放在、2i 以及2i+1 ⎣i / 2⎦ 分量中。

图6-3 顺序存储结构
存储结构二叉树 
这样的存储方式对于满二叉树和全然二叉树是非常合适也是高效方便的。由于满二叉树和全然二叉树採用顺序存储结构既不浪费空间,也能够依据公式非常快的确定结点之间的关系。
可是对于一般的二叉树而言,必须用“虚结点”将一棵二叉树补成一棵全然二叉树来存储,否则无法确定结点之间的前驱兴许关系,可是这样一来就会造成空间的浪费。一种极端的情况是,为了存储k个结点,须要2k-1 个存储单元。图6- 4 说明了这一情况。此时存储空间浪费巨大,这是顺序存储结构的一个缺点。


图6-4 单支二叉树的顺序存储结构
存储结构二叉树 
链式存储结构
设计不同的结点结构可构成不同的链式存储结构。

在二叉树中每一个结点都有两个孩子,则能够设计每一个结点至少包含3 个域:数据域、左孩子域和右孩子域。数据域存放数据元素,

左孩子域存放指向左孩子结点的指针。右孩子域存放指向右孩子结点的指针。如图6-5(a)所看到的。

利用此结点结构得到的二叉树存储结构称为二叉链表。easy证明在具有n 个结点的二叉链表中有n+1 个空链域。

图6-5 二叉树的链式存储结构
存储结构二叉树 
为了方便找到父结点,能够在上述结点结构中添加一个指针域,指向结点的父结点。

如图6-5(b)所看到的。

採用此结点结构得到的二叉树存储结构称为三叉链表。在具有n 个结点的三叉链表中也有n+1 个空链域。

不同的存储结构实现二叉树操作的方法也不同。比如要找某个结点的父结点,在三叉链表中非常easy实现;在二叉链表中则需从根结点出发一一查找。

在实际应用中,要依据二叉树的主要操作来选择存储结构。

为了方便的找到父结点。我们以三叉链表作为二叉树的存储结构。而且在6.3 节中,二叉树的基本操作的实现也是基于三叉链表来实现的。以下我们首先给出具有四个域的结点结构的定义。

代码6-1 二叉树存储结构结点定义
public class BinTreeNode implements Node {
private Object data; //数据域
private BinTreeNode parent; //父结点
private BinTreeNode lChild; //左孩子
private BinTreeNode rChild; //右孩子
private int height; //以该结点为根的子树的高度
private int size; //该结点子孙数(包含结点本身)
102
public BinTreeNode() { this(null); }
public BinTreeNode(Object e) {
data = e; height = 0; size = 1;
parent = lChild = rChild = null;
}
/******Node 接口方法******/
public Object getData() { return data; }
public void setData(Object obj) { data = obj;}
/******辅助方法,推断当前结点位置情况******/
//推断是否有父亲
public boolean hasParent(){ return parent!=null;}
//推断是否有左孩子
public boolean hasLChild(){ return lChild!=null;}
//推断是否有右孩子
public boolean hasRChild(){ return rChild!=null;}
//推断是否为叶子结点
public boolean isLeaf(){ return !hasLChild()&&!hasRChild();}
//推断是否为某结点的左孩子
public boolean isLChild(){ return (hasParent()&&this==parent.lChild);}
//推断是否为某结点的右孩子
public boolean isRChild(){ return (hasParent()&&this==parent.rChild);}
/******与height 相关的方法******/
//取结点的高度,即以该结点为根的树的高度
public int getHeight() { return height; }
//更新当前结点及其祖先的高度
public void updateHeight(){
int newH = 0;//新高度初始化为0,高度等于左右子树高度加1 中的大者
if (hasLChild()) newH = Math.max(newH,1+getLChild().getHeight());
if (hasRChild()) newH = Math.max(newH,1+getRChild().getHeight());
if (newH==height) return; //高度没有发生变化则直接返回
height = newH; //否则更新高度
if (hasParent()) getParent().updateHeight(); //递归更新祖先的高度
}
/******与size 相关的方法******/
//取以该结点为根的树的结点数
public int getSize() { return size; }
//更新当前结点及其祖先的子孙数
public void updateSize(){
size = 1; //初始化为1,结点本身
if (hasLChild()) size += getLChild().getSize(); //加上左子树规模
if (hasRChild()) size += getRChild().getSize(); //加上右子树规模
if (hasParent()) getParent().updateSize(); //递归更新祖先的规模
}
/******与parent 相关的方法******/
//取父结点
public BinTreeNode getParent() { return parent; }
//断开与父亲的关系
public void sever(){
if (!hasParent()) return;
if (isLChild()) parent.lChild = null;
else parent.rChild = null;
parent.updateHeight(); //更新父结点及其祖先高度
parent.updateSize(); //更新父结点及其祖先规模
parent = null;
}
/******与lChild 相关的方法******/
//取左孩子
public BinTreeNode getLChild() { return lChild; }
//设置当前结点的左孩子,返回原左孩子
public BinTreeNode setLChild(BinTreeNode lc){
BinTreeNode oldLC = this.lChild;
if (hasLChild()) { lChild.sever();} //断开当前左孩子与结点的关系
if (lc!=null){
lc.sever(); //断开lc 与其父结点的关系
this.lChild = lc; //确定父子关系
lc.parent = this;
this.updateHeight(); //更新当前结点及其祖先高度
this.updateSize(); //更新当前结点及其祖先规模
}
return oldLC; //返回原左孩子
}
/******与rChild 相关的方法******/
//取右孩子
public BinTreeNode getRChild() { return rChild; }
//设置当前结点的右孩子,返回原右孩子
public BinTreeNode setRChild(BinTreeNode rc){
BinTreeNode oldRC = this.rChild;
if (hasRChild()) { rChild.sever();} //断开当前右孩子与结点的关系
if (rc!=null){
rc.sever(); //断开lc 与其父结点的关系
this.rChild = rc; //确定父子关系
104
rc.parent = this;
this.updateHeight(); //更新当前结点及其祖先高度
this.updateSize(); //更新当前结点及其祖先规模
}
return oldRC; //返回原右孩子
}
}
代码6-1 说明:代码中推断当前结点位置情况的辅助方法以及简单的get 方法都在常数时间内能够完毕。实现也对应很easy。以下主要讨论updateHeight ()、updateSize ()、sever()、setLChild(lc)、getRChild(rc)的实现与时间复杂度。
⑴ updateHeight ():若当前结点v 的孩子发生变化,就须要使用updateHeight ()方法更新当前结点及其祖先结点的高度。请注意。由于一个结点的高度发生变化,会影响到其祖先结点的高度,在这里我们同意直接对不论什么结点运行这一操作。

由于在二叉树中不论什么一个结点的高度,都等于其左右子树的高度中大者加1,而左右子树的高度仅仅须要获取该结点左右孩子的高度就可以获得,仅仅须要Θ(1)时间。续而从v 出发沿parent 引用逆行向上,依次更新各祖先结点的高度就可以。

假设在上述过程中。发现某个结点的高度没有发生变化,算法能够直接终止。综上所述。当对一个结点v 调用updateHeight ()方法时,若v 的层数为level(v),则最多仅仅须要更新level(v)+1 个结点的高度。因此算法的时

间复杂度T(n) = Ο(level(v))。
⑵ updateSize ():相同假设结点v 的孩子发生变化,应该更新当前结点以及其祖先的规模。

由于在二叉树中不论什么一个结点的规模。都等于其左右子树的规模之和加上结点自身,而左右子树的规模仅仅须要获取该结点左右孩子的规模就可以获得,仅仅须要Θ(1)时间。

因此算法的时间复杂度T(n) = Ο(level(v))。

⑶ sever():切断结点v 与父结点p 之间的关系。

该算法须要改动v 与p 的指针域,须要常数时间。除此之外因为p 结点的孩子发生了变化,因此须要调用updateHeight ()和updateSize ()来更新父结点p 及其祖先的高度与规模。其时间复杂度T(n) = Ο(level(v))。

⑷ setLChild(lc)、getRChild(rc):两个算法的功能相对,一个是设置结点v 的左孩子。
一个是设置结点v 的右孩子。

两个算法的实现是类似的,以setLChild()为例说明。首先,假设v 有左孩子oldLC。则应当调用oldLC. sever()断开v 与其左孩子的关系。

其次,调用lc. sever()断开其与父结点的关系。

最后,建立v 与lc 之间的父子关系,并调用v. updateSize ()与v.updateHeight ()更新v 及其祖先的规模与高度。

很多其它精彩内容请关注:http://bbs.superwu.cn
关注超人学院微信二维码:存储结构二叉树
关注超人学院java免费学习交流群:存储结构二叉树

版权声明:本文博主原创文章。博客,未经同意不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/117027.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • rpcbind

    rpcbind一、介绍通俗的来说,rpcbind是NFS中用来进行消息通知的服务。一般情况下rpcbind运行在111端口。并且NFS配置开启rpcbind_enable="YES"

    2022年7月3日
    33
  • Drupal教程之安装篇

    Drupal教程之安装篇星期一,01/12/2009—似曾相识文章地址:[url]http://www.drupaluser.org/node/3[/url]象许多CMS一样,Drupal也是需要安装,其主要步骤如下(以[url]http://drupaluser.org/[/url]为例):1.在[url]http://drupaluser.org/[/u…

    2022年6月8日
    26
  • 项目管理书籍推荐「建议收藏」

    项目管理书籍推荐「建议收藏」人人都是产品经理作为一名北漂,我的同事郝文鹏曾经总结过一些自己的经验,现无私分享出来,希望能帮到你:IT项目管理作为项目管理的子集,建议先看一些项目管理的书籍作为基础。基础类:《项目管理:计划.进

    2022年8月4日
    3
  • 万能乘法速算法大全_小学数学各年级知识点和重点、难点大全,复习必备提纲!…

    万能乘法速算法大全_小学数学各年级知识点和重点、难点大全,复习必备提纲!…今天小数老师为不同年级的学生整理出小学数学重要知识点帮助小伙伴们及时查缺补漏哦!一年级的知识重点1数与计算(1)20以内数的认识,加法和减法。数数。数的组成、顺序、大小、读法和写法。加法和减法。连加、连减和加减混合式题(2)100以内数的认识。加法和减法。数数。个位、十位。数的顺序、大小、读法和写法。两位数加、减整十数和两位数加、减一位数的口算。两步计算的加减式题。2量与计量钟面的认识(…

    2022年6月7日
    61
  • 第k短路径_典型的分类算法K均值

    第k短路径_典型的分类算法K均值给定一张 N 个点(编号 1,2…N),M 条边的有向图,求从起点 S 到终点 T 的第 K 短路的长度,路径允许重复经过点或边。注意: 每条最短路中至少要包含一条边。输入格式第一行包含两个整数 N 和 M。接下来 M 行,每行包含三个整数 A,B 和 L,表示点 A 与点 B 之间存在有向边,且边长为 L。最后一行包含三个整数 S,T 和 K,分别表示起点 S,终点 T 和第 K 短路。输出格式输出占一行,包含一个整数,表示第 K 短路的长度,如果第 K 短路不存在,则输出 −1。数据范围

    2022年8月10日
    2
  • java版我的世界下载_我的世界java版

    java版我的世界下载_我的世界java版我的世界java版这个所谓的java版可能大家不是很熟悉,java版就是《我的世界》是整个游戏的初始版本,目前Java版本是全平台游戏版本中内容最多,更新速度最快的版本。此外,Java版本拥有大规模的全球玩家社区,得益于它可拓展的特性,拥有百万件玩家创意作品,这些都使得Java版本是核心玩家最喜爱的版本之一。我的世界java版手机版种子:出生点前有丛林神庙的地图种子在这个种子地图上,你会出生在一座…

    2022年7月8日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号