6.2 基本操作与存储
二叉树的存储
1.顺序存储结构
所谓二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点。一般是按照二叉树结点从上至下、从左到右的顺序存储。
这样结点在存储位置上的前驱后继关系并不一定就是它们在逻辑上的邻接关系,然而只有通过一些方法确定某结点在逻辑上的前驱结点和后继结点,这种存储才有意义。因此,依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。图6.4 给出的图6.3(a)所示的完全二叉树的顺序存储示意图。
对于一般的二叉树,如果仍按从上至下和从左到右的顺序将树中的结点顺序存储在一维数组中,则数组元素下标之间的关系不能够反映二叉树中结点之间的逻辑关系,只有增添一些并不存在的空结点,使之成为一棵完全二叉树的形式,然后再用一维数组顺序存储。
如图6.5 给出了一棵一般二叉树改造后的完全二叉树形态和其顺序存储状态示意图。显然,这种存储对于需增加许多空结点才能将一棵二叉树改造成为一棵完全二叉树的存储时,会造成空间的大量浪费,不宜用顺序存储结构。最坏的情况是右单支树,如图6.6 所示,一棵深度为k 的右单支树,只有k 个结点,却需分配2k-1 个存储单元。
(c) 单支树改造后完全二叉树的顺序存储状态图6.6 右单支二叉树及其顺序存储示意图二叉树的顺序存储表示可描述为:
1 #define MAXNODE /*二叉树的最大结点数*/ 2 typedef elemtype SqBiTree[MAXNODE] /*0 号单元存放根结点*/ 3 SqBiTree bt;
即将bt 定义为含有MAXNODE 个elemtype 类型元素的一维数组。
2.链式存储结构
所谓二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示着元素的逻辑关系。通常有下面两种形式。
(1)二叉链表存储
链表中每个结点由三个域组成,除了数据域外,还有两个指针域,分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。结点的存储的结构为:
其中,data 域存放某结点的数据信息;lchild 与rchild 分别存放指向左孩子和右孩子的指针,当左孩子或右孩子不存在时,相应指针域值为空(用符号∧或NULL 表示)。
图6.7(a)给出了图6.3(b)所示的一棵二叉树的二叉链表示。二叉链表也可以带头结点的方式存放,如图6.7(b)所示。
(2)三叉链表存储
每个结点由四个域组成,具体结构为:
其中,data、lchild 以及rchild 三个域的意义同二叉链表结构;parent 域为指向该结点双亲结点的指针。这种存储结构既便于查找孩子结点,又便于查找双亲结点;但是,相对于二叉链表存储结构而言,它增加了空间开销。图6.8 给出了图6.3(b)所示的一棵二叉树的三叉链表示。
尽管在二叉链表中无法由结点直接找到其双亲,但由于二叉链表结构灵活,操作方便,对于一般情况的二叉树,甚至比顺序存储结构还节省空间。因此,二叉链表是最常用的二叉树存储方式。本书后面所涉及到的二叉树的链式存储结构不加特别说明的都是指二叉链表结构。
二叉树的二叉链表存储表示可描述为:
1 typedef struct BiTNode 2 { 3 elemtype data; 4 struct BiTNode *lchild;*rchild; /*左右孩子指针*/ 5 }BiTNode,*BiTree;
即将BiTree 定义为指向二叉链表结点结构的指针类型。
二叉树的基本操作及实现
1.二叉树的基本操作
二叉树的基本操作通常有以下几种:
(1)Initiate(bt)建立一棵空二叉树。
(2)Create(x,lbt,rbt)生成一棵以x 为根结点的数据域信息,以二叉树lbt 和rbt为左子树和右子树的二叉树。
(3)InsertL(bt,x,parent)将数据域信息为x 的结点插入到二叉树bt 中作为结点parent 的左孩子结点。如果结点parent 原来有左孩子结点,则将结点parent 原来的左孩子结点作为结点x 的左孩子结点。
(4)InsertR(bt,x,parent)将数据域信息为x 的结点插入到二叉树bt 中作为结点parent 的右孩子结点。如果结点parent 原来有右孩子结点,则将结点parent 原来的右孩子结点作为结点x 的右孩子结点。
(5)DeleteL(bt,parent)在二叉树bt 中删除结点parent 的左子树。
(6)DeleteR(bt,parent)在二叉树bt 中删除结点parent 的右子树。
(7)Search(bt,x)在二叉树bt 中查找数据元素x。
(8)Traverse(bt)按某种方式遍历二叉树bt 的全部结点。
2.算法的实现
算法的实现依赖于具体的存储结构,当二叉树采用不同的存储结构时,上述各种操作的实现算法是不同的。下面讨论基于二叉链表存储结构的上述操作的实现算法。
(1)Initiate(bt)初始建立二叉树bt,并使bt 指向头结点。在二叉树根结点前建立头结点,就如同在单链表前建立的头结点,可以方便后边的一些操作实现。
1 int Initiate (BiTree *bt) 2 { 3 /*初始化建立二叉树*bt 的头结点*/ 4 if((*bt=(BiTNode *)malloc(sizeof(BiTNode)))==NULL) 5 return 0; 6 *bt->lchild=NULL; 7 *bt->rchild=NULL; 8 return 1; 9 }
算法6.1
(2)Create(x,lbt,rbt)建立一棵以x 为根结点的数据域信息,以二叉树lbt 和rbt为左右子树的二叉树。
建立成功时返回所建二叉树结点的指针;建立失败时返回空指针。
1 BiTree Create(elemtype x,BiTree lbt,BiTree rbt) 2 { 3 /*生成一棵以x 为根结点的数据域值以lbt 和rbt 为左右子树的二叉树*/ 4 BiTree p; 5 if ((p=(BiTNode *)malloc(sizeof(BiTNode)))==NULL) return NULL; 6 p->data=x; 7 p->lchild=lbt; 8 p->rchild=rbt; 9 return p; 10 }
算法6.2
(3)InsertL(bt,x,parent)
1 BiTree InsertL(BiTree bt,elemtype x,BiTree parent) 2 { 3 /*在二叉树bt 的结点parent 的左子树插入结点数据元素x*/ 4 BiTree p; 5 if (parent==NULL) 6 { 7 printf(“\n 插入出错”); 8 return NULL; 9 } 10 if ((p=(BiTNode *)malloc(sizeof(BiTNode)))==NULL) 11 return NULL; 12 p->data=x; 13 p->lchild=NULL; 14 p->rchild=NULL; 15 if (parent->lchild==NULL) 16 parent->lchild=p; 17 else 18 { 19 p->lchild=parent->lchild; 20 parent->lchild=p; 21 } 22 return bt; 23 }
算法6.3
(4)InsertR(bt,x,parent)功能类同于(3),算法略。
(5)DeleteL(bt,parent)在二叉树bt 中删除结点parent 的左子树。当parent 或parent的左孩子结点为空时删除失败。删除成功时返回根结点指针;删除失败时返回空指针。
1 BiTree DeleteL(BiTree bt,BiTree parent) 2 { 3 /*在二叉树bt 中删除结点parent 的左子树*/ 4 BiTree p; 5 if (parent==NULL||parent->lchild==NULL) 6 { 7 printf(“\n 删除出错”); 8 return NULL; 9 } 10 p=parent->lchild; 11 parent->lchild=NULL; 12 free(p); /*当p 为非叶子结点时,这样删除仅释放了所删子树根结点的空间,*/ 13 /*若要删除子树分支中的结点,需用后面介绍的遍历操作来实现。*/ 14 return bt; 15 }
算法6.4
(6)DeleteR(bt,parent)功能类同于(5),只是删除结点parent 的右子树。算法略。操作Search(bt,x)实际是遍历操作Traverse(bt)的特例,关于二叉树的遍历操作的实现,将在下一节中重点介绍。
posted on 2015-06-17 15:30 chunlanse2014 阅读(358) 评论(0) 编辑 收藏 举报