存储结构二叉树

二叉树的存储结构有两种:顺序存储结构和链式存储结构。

顺序存储结构

对于满二叉树和全然二叉树来说,能够将其数据元素逐层存放到一组连续的存储单元中,如图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免费学习交流群:

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

时间: 2024-07-30 03:23:51

存储结构二叉树的相关文章

【数据结构】树与树的表示、二叉树存储结构及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树与哈夫曼编码、集合及其运算

1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找--方法一:顺序查找(时间复杂度O(n)) int SequentialSearch(St

二叉树的二叉链表存储结构

一.二叉树的二叉链表存储结构 二叉树的二叉链表存储结构及其操作应用广泛,各大IT公司面试的时候都很喜欢考察二叉树的奇异操作,但是万变不离其宗,只要熟练掌握二叉树的二叉链表存储结构及其基本操作,其它奇异操作根据需要进行变换即可.如下所示: typedef char TElemType; TElemType Nil = ' '; typedef struct BiTNode { TElemType data; // 结点的值 BiTNode *lchild, *rchild; // 左右孩子指针 }

数据结构--树(上)-- 二叉树及存储结构

二叉树及存储结构 二叉树的定义:一个有穷的结点集合.若不为空,则它是由根节点和称为其左子树和右子树的两个不想交的二叉树组成 一般的左右的树是没有左右之分的.二叉树有左右之分. 三种特殊的二叉树 斜二叉树         实质就可以是链表了. 完美二叉树 完全二叉树 二叉树的几个重要的性质 二叉树的抽象数据类型 对二叉树来讲,最重要的就是Traversal() 遍历,讲二叉树基本就讲遍历了. 二叉树的存储结构 顺序存储结构 顺序存储结构 可以存储完全二叉树:从上往下,从左往右,来进行便利.把这种树

二叉树及存储结构

本文的结构: 二叉树的基本形态 二叉树的重要性质 二叉树的抽象数据类型定义 二叉树的存储结构 二叉树T:一个有穷的节点集合.这个集合可以为空,若不为空,则它是由根节点和称为其左子树TL和右子树TR的两个不相交的二叉树组成 二叉树的五种基本形态: (a) 空树 (b) 有一个结点 (c) 右子树为空 (d) 左子树为空 (e) 有左右子树 二叉树的重要性质: 一个二叉树第i层的最大节点数为:2i-1,i >=1; 深度为k的二叉树有最大结点总数为 2k-1,k>=1: 对任何非空二叉树T,若n0

二叉树的链式存储结构----二叉链表

头文件:head.h #include<string.h> #include<ctype.h> #include<malloc.h> /* malloc()等 */ #include<limits.h> /* INT_MAX等 */ #include<stdio.h> /* EOF(=^Z或F6),NULL */ #include<stdlib.h> /* atoi() */ #include<io.h> /* eof()

浅谈数据结构之二叉树存储结构实现(七)

树:是n个结点的有限集:n=0时称为空树.在任意一棵非空树中,有且只有一个特定的结点称为根结点:其余的结点可分为m(m>0)个互不相交的有限集,其中每一个有限集都是一棵子树.结点拥有的子树数称为结点的度:度为0的结点称为叶结点或者终端结点,度不为0的结点称为分支结点或者非终端结点:树的度就是树内各结点的度的最大值. 二叉树的特点有:(1).每个结点最多有两棵子树,所以二叉树不存在度大于2的结点(注意:不是只有两棵子树,而是最多有两棵子树,没有子树或者有一颗子树都是可以的);(2).左子树和右子树

二叉树的链式存储结构--二叉链表

1 二叉树的链式存储结构 //二叉链表的结点结构定义 typedef struct BiTNode { int data; struct BiTNode *lchild; struct BiTNode *rchild; }BiTNode; typedef struct BiTNode *BiTree; 结构示意图如下: 2 二叉树的遍历方法 (1)前序遍历:先访问根结,然后前序遍历左子树,再前序遍历右子树. (2)

_DataStructure_C_Impl:二叉树的二叉链表存储结构

// _DataStructure_C_Impl: #include<stdio.h> #include<stdlib.h> #define MaxSize 100 typedef char DataType; typedef struct Node{ //二叉链表存储结构类型定义 DataType data; //数据域 struct Node *lchild; //指向左孩子结点 struct Node *rchild; //指向右孩子结点 }*BiTree,BitNode;

二叉树链式存储结构

二叉链表的C语言描述 基本运算的算法--建立二叉链表.先序遍历二叉树.中序遍历二叉树.后序遍历二叉树.后序遍历求二叉树深度 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65