一、树的若干术语
1)结点:包括一个数据元素及若干指向其子树的分支;
2)结点的度:结点所拥有的子树的个数;
3)叶结点:度为0的结点,也称作终端结点;
4)分支结点:度不为0的结点;
5)孩子结点:树中一个结点的子树的根结点,也叫后继结点;
6)双亲结点:若树中某结点有孩子结点,则这个结点就 称作它的孩子结点的双亲结点 ;
7)兄弟结点:具有相同的双亲结点的结点;
8)树的度:树中所有结点的度的最大值;
9)结点的层次:从根结点到树中某结点所经路径上的分支数,根节点层次规定为0;
10)树的深度:树中所有结点的层次的最大值;
11)无序树:树中任意一个结点的各孩子结点之间的次序构成无关紧要的树 ;
12)有序树:树中任意一个结点的各孩子结点有严格排列次序的树;
13)森林: m(m≥0)棵树的集合。
二、树的存储结构
从结点之间的逻辑关系分,树的存储结构主要有四种组合:
1)双亲表示法
2)孩子表示法
3)双亲孩子表示法
4)孩子兄弟表示法
三、 二叉树
二叉树的定义
定义:一个根结点和两个不相交的、分别称为左子树和右子树的二叉树组成 。
逻辑结构: 一对二(1:2)
基本特征:
① 每个结点最多只有两棵子树(不存在度大于2的结点);
② 左子树和右子树次序不能颠倒。所以下面是两棵不同的树(注意:二叉树不是有序树)
满二叉树:
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称为满二叉树。
完全二叉树:
如果一棵深度为k,有n个结点的二叉树中各结点能够与深度为k的顺序编号的满二叉树从1到n标号的结点相对应的二叉树称为完全二叉树。如下图所示
二叉树的性质
性质1: 若规定根结点的层数为0,则在一棵非空二叉树的第i层上至多有2^i个结点(i≥0)。
性质2: 深度为k的二叉树至多有2^(k+1)-1个结点。
说明:深度k=-1,表示没有一个结点;深度k=0,表示只有一个根结点。
性质3: 具有n个结点的完全二叉树的深度k为log2(n+1) -1(大于或等于log2(n+1) -1 的最小整数)。
性质4 对于一棵非空的二叉树,如果叶结点个数为n0,度为2的结点数为n2,则有n0= n2+1。
性质5: 对于一棵有n个结点的完全二叉树,按照从上至下和从左至右的顺序对所有结点从0开始顺序编号,则对于序号为i的结点(0≤i < n),有:
(1)如果i>0,则其双亲是结点(i-1)/2(“/”表示整除) ;如果i=0,则结点i是根结点,无双亲。
(2)如果2i+1<n ,其左孩子是结点2i+1;如果2i+1≥n,则结点i无左孩子。
(3)如果2i+2<n,其右孩子是结点2i+2;如果2i+2≥n,则结点i无右孩子.
此性质告诉我们,如果把完全二叉树按照从上到下,从左到右的顺序对所有结点顺序编号,则可以用一维数组存储完全二叉树。此时,完全二叉树中任意结点的双亲结点下标、左孩子结点下标和右孩子结点下标可以计算出来。
二叉树的三种存储结构
顺序存储结构
链式存储结构
仿真指针存储结构。
(1) 二叉树的顺序存储结构
完全二叉树的结点可按从上至下和从左至右的次序存储在一维数组中,其结点之间的关系可由公式计算得到,这就是二叉树的顺序存储结构。下面两棵树在数组中的存储结构分别为:
对于一般的非完全二叉树显然不能直接使用二叉树的顺序存储结构。可以首先在非完全二叉树中增添一些并不存在的空结点使之变成完全二叉树的形态,然后再用顺序存储结构存储。
(2)二叉树的链式存储结构
二叉树的链式存储结构是用指针建立二叉树中结点之间的关系。二叉树最常用的的链式存储结构是二叉链。
二叉链存储结构的每个结点包含三个域,分别是数据域data、左孩子指针域leftChild和右孩子指针域rightChild。二叉链存储结构中每个结点的图示结构为:
二叉链存储结构的二叉树也有不带头结点和带头结点两种。带头结点的二叉链存储结构的二叉树见下图(b),不带头结点的二叉链存储结构的二叉树如下图(a)所示。
(3)二叉树的仿真指针
二叉树的仿真指针存储结构是用数组存储二叉树中的结点,数组中每个结点除数据元素域外,再增加仿真指针域用于仿真常规指针建立二叉树中结点之间的关系。
二叉树的操作设计
//结构体设计
typedef struct Node
{
DataType data; //数据域
struct Node *leftChild; //左子树指针
struct Node *rightChild; //右子树指针
}BiTreeNode;
/*初始化*/
void Initiate(BiTreeNode **root) //初始化建立二叉树的头结点
{
*root = (BiTreeNode *)malloc(sizeof(BiTreeNode));
(*root)->leftChild = NULL;
(*root)->rightChild = NULL;
}
/*左子树插入结点,返回插入的结点*/
BiTreeNode *InsertLeftNode(BiTreeNode *curr, DataType x)
{
BiTreeNode *s, *t;
if(curr == NULL) return NULL;
t = curr->leftChild; //保存原curr所指结点的左子树指针
s = (BiTreeNode *)malloc(sizeof(BiTreeNode));
s->data = x;
s->leftChild = t; //新插入结点的左子树为原curr的左子树
s->rightChild = NULL;
curr->leftChild = s; //新结点为curr的左子树
return curr->leftChild;
}
/*右子树插入结点*/
BiTreeNode *InsertRightNode(BiTreeNode *curr, DataType x)
{
BiTreeNode *s, *t;
if(curr == NULL) return NULL;
t = curr->rightChild; //保存原curr所指结点的右子树指针
s = (BiTreeNode *)malloc(sizeof(BiTreeNode));
s->data = x;
s->rightChild = t; //新插入结点的右子树为原curr的右子树
s->leftChild = NULL;
curr->rightChild = s; //新结点为curr的右子树
return curr->rightChild;
}
/*删除左子树*/
BiTreeNode *DeleteLeftTree(BiTreeNode *curr)
{
if(curr == NULL || curr->leftChild == NULL)
return NULL;
Destroy(&curr->leftChild); //撤销操作
curr->leftChild = NULL;
return curr;
}
/*删除右子树*/
BiTreeNode *DeleteRightTree(BiTreeNode *curr)
{
if(curr == NULL || curr->rightChild == NULL)
return NULL;
Destroy(&curr->rightChild); //撤销操作
curr->rightChild = NULL;
return curr;
}
将上诉文件放在Bitree.h中,编写一个建立如图所示的带头结点的二叉链存储结构二叉树的程序。
#include <stdlib.h>
#include <stdio.h>
typedef char DataType;
#include "Bitree.h"
Void main(void)
{ BiTreeNode *root, *p;
Initiate(&root);
p = InsertLeftNode(root, 'A');
p = InsertLeftNode(p, 'B');
p = InsertLeftNode(p, 'D');
p = InsertRightNode(p, 'G');
p = InsertRightNode(root->leftChild, 'C');
InsertLeftNode(p, 'E');
InsertRightNode(p, 'F');
}