1. 树的相关概念
根节点(Root):树的顶部节点
子节点(Child):离开根节点时直接连接到另一个节点的节点。
叶子节点(Leaf):没有子节点的节点
边(Edge):一个节点与另一个节点之间的连接。
路径(Path):连接节点与子代节点的节点和边的序列。
节点高度(Height):节点和叶子之间最长路径上的边的数量
注意:叶子节点的高度为0,如果树只有一个节点,那么这个节点的高也是0
深度(Depth):从该节点到树的根节点的边数总和。
注意:
需要注意的是根节点的深度(Depth)是0.
从高度和深度的对比,它们的方向刚好是相反的。
度:节点的子树数目
层级(Level):该节点到树的根节点的最长路径的边数总和+1。
双亲节: 树的双亲节点是一个节点,如上图中,4的双亲节点是2
二叉树: 二叉树是一个每个最结最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。二叉树节点的度最大也就是2,而普通的树,节点的度是没有限制的。
完美/满二叉树:
- 所有的几点都包含两个子节点
- 所有的叶子节点的Height或者Level都相等
完全二叉树:最后一层都是满的(都有两个子节点),并且最后一层的节点是从左往右排列的。
通俗的讲:节点按层从左往右排列。最后一层排满了就是完美二叉树,没有满则是完全二叉树。
完美二叉树一定是完全二叉树,完全二叉树不一定是完美二叉树。
完满二叉树:每个节点都有两个子节点
2. 二叉树的顺序存储
2.1 顺序二叉树的基本操作
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */
typedef int Status; // Status是函数的类型
typedef int CElemType; /* 树结点的数据类型,目前暂定为整型 */
typedef CElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点 */
CElemType Nill = 0; /*设整型以0为空 或者以 INT_MAX(65535)*/
typedef struct {
int level; // 结点层
int order; // 本层的序号 安装满二叉树给定序号规则
}Position;
// ✅初始化空树
Status InitBiTree(SqBiTree T) {
for (int i = 0; i < MAX_TREE_SIZE; i++) {
//将二叉树初始化值置空
T[i] = Nill;
}
return OK;
}
// ✅访问
Status visit(CElemType c){
printf("%d ",c);
return OK;
}
// ✅那层序次序输入二叉树中的结点值,构造二叉树
Status CreatBiTree(SqBiTree T) {
int i = 0;
while (i < 10) {
T[i] = i + 1;
// 出现结点不为空,双亲节点为空
if (i != 0 && T[(i+1)/2-1] == Nill && T[i] == Nill) {
printf("出现无双亲的非根结点%d\n",T[i]);
}
i++;
}
// 将空值赋值给后面的节点
while (i < MAX_TREE_SIZE) {
T[i] = Nill;
i++;
}
return OK;
}
// ✅清空
// 如果要2个函数的结果一样,但是目的不同;
// 在顺序存储结构中, 两个函数完全一样的结果
#define ClearBiTree InitBiTree
// ✅判断二叉树是否为空,判断根节点
Status BiTreeEmpty(SqBiTree T){
if (T[0] == Nill) {
return TRUE;
}
return FALSE;
}
// ✅获取二叉树深度
int BiTreeDepth(SqBiTree T)
{
int j = -1;
int i;
// 找到最后一个结点
for (i = MAX_TREE_SIZE; i >= 0; i--) {
if (T[i] != Nill) {
break;
}
}
do {
j++;
} while (powl(2, j) <= i);//计算2的次幂
return j;
}
// ✅返回处于位置e(层,本层序号)的结点值
CElemType Value(SqBiTree T, Position e){
//pow(2,e.level-1) 找到层序
printf("%d\n",(int)pow(2,e.level-1));
//e.order
printf("%d\n",e.order);
//4+2-2;
return T[(int)pow(2, e.level-1)+e.order-2];
}
// ✅获取二叉树跟结点的值
Status Root(SqBiTree T,CElemType *e){
if (BiTreeEmpty(T)) {
return ERROR;
}
*e = T[0];
return OK;
}
// ✅给处于位置e的结点赋值
Status Assign(SqBiTree T,Position e,CElemType value) {
//找到当前e在数组中的具体位置索引
int i = (int)pow(2, e.level-1)+e.order-2;
// 判断双亲节点为空
if (value != Nill && T[(i+1)/2-1] == Nill) {
return ERROR;
}
//判断自己为空,但是有叶子结点
if (value == Nill && (T[i*2+1] != Nill || T[i*2+2] != Nill)) {
return ERROR;
}
T[i] = value;
return OK;
}
// ✅获取e的双亲
CElemType Parent(SqBiTree T, CElemType e){
// 空树
if (T[0] == Nill) {
return Nill;
}
for (int i = 1 ; i < MAX_TREE_SIZE; i++) {
//找到e
if (T[i] == e) {
return T[(i+1)/2 - 1];
}
}
//没有找到
return Nill;
}
// ✅获取某个结点的左孩子
CElemType LeftChild(SqBiTree T,CElemType e){
// 空树
if (T[0] == Nill) {
return Nill;
}
for (int i = 0; i < MAX_TREE_SIZE; i++) {
//找到e
if (T[i] == e) {
return T[i*2 + 1];
}
}
return Nill;
}
// ✅获取某个结点的右孩子
CElemType RightChild(SqBiTree T,CElemType e){
//空树
if (T[0] == Nill) {
return Nill;
}
for (int i = 0 ; i < MAX_TREE_SIZE-1; i++) {
//找到e
if (T[i] == e) {
return T[i*2+2];
}
}
//没有找到
return Nill;
}
2.2 二叉树的遍历
二叉树的遍历分为以下几种情况:
-
前序遍历
规则:若二叉树为空,则空操作返回,否则,先访问根节点,然后前序遍历左子树,再前序遍历右子树
void PreTraverse(SqBiTree T,int e){
//打印结点数据
visit(T[e]);
//先序遍历左子树
if (T[2 * e + 1] != Nil) {
PreTraverse(T, 2*e+1);
}
//最后先序遍历右子树
if (T[2 * e + 2] != Nil) {
PreTraverse(T, 2*e+2);
}
}
Status PreOrderTraverse(SqBiTree T){
//树不为空
if (!BiTreeEmpty(T)) {
PreTraverse(T, 0);
}
printf("\n");
return OK;
}
-
中序遍历
规则:若二叉树为空,则空操作返回,否则,先根节点开始(注意不是先访问根节点),然后中序遍历左子树,然后访问根节,最后再中序遍历右子树
void InTraverse(SqBiTree T, int e){
/* 左子树不空 */
if (T[2*e+1] != Nil)
InTraverse(T, 2*e+1);
visit(T[e]);
/* 右子树不空 */
if (T[2*e+2] != Nil)
InTraverse(T, 2*e+2);
}
Status InOrderTraverse(SqBiTree T){
/* 树不空 */
if (!BiTreeEmpty(T)) {
InTraverse(T, 0);
}
printf("\n");
return OK;
}
-
后续遍历
规则:若二叉树为空,则空操作返回,否则,从左到右先叶子结点后结点的方式遍历左右子树,最后访问根节点
void PostTraverse(SqBiTree T,int e)
{ /* 左子树不空 */
if(T[2*e+1]!=Nil)
PostTraverse(T,2*e+1);
/* 右子树不空 */
if(T[2*e+2]!=Nil)
PostTraverse(T,2*e+2);
visit(T[e]);
}
Status PostOrderTraverse(SqBiTree T)
{
if(!BiTreeEmpty(T)) /* 树不空 */
PostTraverse(T,0);
printf("\n");
return OK;
}
-
层序遍历
规则:若二叉树为空,则空操作返回,否则,从树的第一层,也是就根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个遍历
void LevelOrderTraverse(SqBiTree T){
int i = MAX_TREE_SIZE-1;
//找到最后一个非空结点的序号
while (T[i] == Nill) i--;
//从根结点起,按层序遍历二叉树
for (int j = 0; j <= i; j++)
//只遍历非空结点
if (T[j] != Nill)
visit(T[j]);
printf("\n");
}
3. 二叉树的链式存储
3.1 链式二叉树的基本操作
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
/* 存储空间初始分配量 */
#define MAXSIZE 100
/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Status;
#pragma mark--二叉树构造
int indexs = 1;
typedef char String[24]; /* 0号单元存放串的长度 */
String str;
Status StrAssign(String T,char *chars)
{
int i;
if(strlen(chars)>MAXSIZE)
return ERROR;
else
{
T[0]=strlen(chars);
for(i=1;i<=T[0];i++)
T[i]=*(chars+i-1);
return OK;
}
}
#pragma mark--二叉树基本操作
typedef char CElemType;
CElemType Nill =' '; /* 字符型以空格符为空 */
typedef struct BiTNode /* 结点结构 */
{
CElemType data; /* 结点数据 */
struct BiTNode *lchild,*rchild; /* 左右孩子指针 */
}BiTNode,*BiTree;
// ✅打印数据
Status visit(CElemType e)
{
printf("%c ",e);
return OK;
}
// ✅构造空二叉树T
Status InitBiTree(BiTree *T)
{
*T=NULL;
return OK;
}
// ✅销毁二叉树
void DestroyBiTree(BiTree *T)
{
if(*T)
{
/* 有左孩子 */
if((*T)->lchild)
DestroyBiTree(&(*T)->lchild); /* 销毁左孩子子树 */
/* 有右孩子 */
if((*T)->rchild)
DestroyBiTree(&(*T)->rchild); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T=NULL; /* 空指针赋0 */
}
}
#define ClearBiTree DestroyBiTree
// ✅创建二叉树
void CreateBiTree(BiTree *T){
CElemType ch;
//获取字符
ch = str[indexs++];
//判断当前字符是否为'#'
if (ch == '#') {
*T = NULL;
}else
{
//创建新的结点
*T = (BiTree)malloc(sizeof(BiTNode));
//是否创建成功
if (!*T) {
exit(OVERFLOW);
}
/* 生成根结点 */
(*T)->data = ch;
/* 构造左子树 */
CreateBiTree(&(*T)->lchild);
/* 构造右子树 */
CreateBiTree(&(*T)->rchild);
}
}
// ✅二叉树T是否为空
Status BiTreeEmpty(BiTree T)
{
if(T)
return FALSE;
else
return TRUE;
}
// ✅二叉树T的深度
int BiTreeDepth(BiTree T){
int i,j;
if(!T)
return 0;
//计算左孩子的深度
if(T->lchild)
i=BiTreeDepth(T->lchild);
else
i=0;
//计算右孩子的深度
if(T->rchild)
j=BiTreeDepth(T->rchild);
else
j=0;
//比较i和j
return i>j?i+1:j+1;
}
// ✅二叉树T的根
CElemType Root(BiTree T){
if (BiTreeEmpty(T))
return Nill;
return T->data;
}
// ✅返回p所指向的结点值
CElemType Value(BiTree p){
return p->data;
}
// ✅给p所指结点赋值为value
void Assign(BiTree p,CElemType value)
{
p->data=value;
}
3.2 链式二叉树的遍历
// ✅前序递归遍历T
void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
PreOrderTraverse(T->lchild); /* 再先序遍历左子树 */
PreOrderTraverse(T->rchild); /* 最后先序遍历右子树 */
}
// ✅中序递归遍历T
void InOrderTraverse(BiTree T)
{
if(T==NULL)
return ;
InOrderTraverse(T->lchild); /* 中序遍历左子树 */
printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
InOrderTraverse(T->rchild); /* 最后中序遍历右子树 */
}
// ✅后序递归遍历T
void PostOrderTraverse(BiTree T)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild); /* 先后序遍历左子树 */
PostOrderTraverse(T->rchild); /* 再后序遍历右子树 */
printf("%c",T->data);/* 显示结点数据,可以更改为其它对结点操作 */
}