树的定义
树(tree):n个节点构成的有点集合。
当n=0时,称为空树;
对于任一颗非空树(n>0),具有以下性质
- 树种有一个称为根的特殊节点,用r表示;
- 其余节点可分为m(m>0)个互不相交的有限集T1…TM,其中每个集合本身又是一棵树,称为原来树的子树(Subtree)
- 子树是不相交的;
- 除了根节点外,每个节点有且仅有一个父节点;
- 一颗N个节点的树有N-1个条边。
树的一些基本术语
- 节点的度:节点的子树个数
- 树的度:树的所有节点中最大的度数
- 叶节点:度为0的节点
- 父节点:有子树的节点是其子树的根节点的父节点
- 子节点:若A节点是B节点的父节点,则称B节点是A节点的父节点;子节点也称孩子节点
- 兄弟节点:具有同一父节点的各个节点是彼此的兄弟节点
- 路径和路径长度:从节点n1到nk的路径为一个节点序列n1,n2,…,nk,相邻节点是子父节点。路径所包含的个数为路径的长度。
- 祖先节点:沿树根跟到某一节点路径上的所有节点都是这个节点的祖先节点
- 子孙节点:某一节点的子树中的所有节点是这个节点的子孙
- 节点的层次:规定根节点在1层,其它任一节点的层数是其父节点的层数加1
- 树的深度:树中所有节点中的最大层次是这颗树的深度
树的孩子兄弟表示法
树结构中,位于同一层的节点之间互为兄弟节点。例如,图 1 的普通树中,节点 A、B 和 C 互为兄弟节点,而节点 D、E 和 F 也互为兄弟节点。孩子兄弟表示法,采用的是链式存储结构,其存储树的实现思想是:从树的根节点开始,依次用链表存储各个节点的孩子节点和兄弟节点。
因此,该链表中的节点应包含以下 3 部分内容
- 节点的值
- 指向孩子节点的指针
- 指向兄弟节点的指针
二叉树的定义
二叉树T:一个有穷的节点集合。这个集合可以为空,若不为空,则它是由根节点和称为其左子树Tr和右子树Tr的两个不相交的二叉树组成。
- 二叉树的五种基本形态
- ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200329224746796.png
特殊二叉树
二叉树的性质
- 一个二叉二叉树第i层的最大节点数为:2^(i-1),i>=1
- 深度为k的二叉树有最大节点总数为:2^k-1,k>=1
- 对于任何非空二叉树T,若n0表示叶节点的个数、n2是度为2的非叶节点个数,
那么两者满足关系n0=n2+1
二叉树的存储结构
顺序存储结构
完全二叉树:按从上至下、从左到右顺序存储n个节点的完全二叉树的节点父子关系
如果一般二叉树采用这种结构存储就会 造成巨大的空间浪费
顺序存储的基本运算
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAX_TREE_SIZE 100 //二叉树的最大结点数
typedef int Status;
typedef int TElemType; // 树结点的数据类型
typedef TElemType SqBiTree[MAX_TREE_SIZE];
typedef struct
{
int level,order; // 结点的层,以及在层中的序号
}Position;
TElemType Nil=0; // 设整型以0为空
//打印元素值
Status visit(TElemType c)
{
printf("%d ",c);
return OK;
}
// 构造空二叉树T
Status InitBiTree(SqBiTree T)
{
int i;
for(i=0;i<MAX_TREE_SIZE;i++)
T[i]=Nil; // 初值为空0
return OK;
}
// 按层序次序输入二叉树中结点的值, 构造顺序存储的二叉树T
Status CreateBiTree(SqBiTree T)
{
int i=0;
printf("请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤%d:\n",MAX_TREE_SIZE);
while(i<10)
{
T[i]=i+1;
if(i!=0&&T[(i+1)/2-1]==Nil&&T[i]!=Nil) // 此结点(不空)无父亲节点且不是根
{
printf("出现无父亲节点的非根结点%d\n",T[i]);
exit(ERROR);
}
i++;
}
while(i<MAX_TREE_SIZE)
{
T[i]=Nil; // 将空赋值给T的后面的结点
i++;
}
return OK;
}
#define ClearBiTree InitBiTree
// 判断二叉树是否为空
Status BiTreeEmpty(SqBiTree T)
{
if(T[0]==Nil) // 根结点为空,则树空
return TRUE;
else
return FALSE;
}
// 返回二叉树的深度
int BiTreeDepth(SqBiTree T)
{
int i,j=-1;
for(i=MAX_TREE_SIZE-1;i>=0;i--) // 找到最后一个结点
if(T[i]!=Nil)
break;
i++;
do
j++;
while(i>=powl(2,j));// 计算2的j次幂
return j;
}
// 用e返回T的根
Status Root(SqBiTree T,TElemType *e)
{
if(BiTreeEmpty(T)) // T空
return ERROR;
else
{
*e=T[0];
return OK;
}
}
// 返回处于位置e(层,本层序号)的结点的值
TElemType Value(SqBiTree T,Position e)
{
return T[(int)powl(2,e.level-1)+e.order-2];
}
// 修改处于位置e(层,本层序号)的结点的值
Status Assign(SqBiTree T,Position e,TElemType value)
{
int i=(int)powl(2,e.level-1)+e.order-2; // 将层、本层序号转为矩阵的序号
if(value!=Nil&&T[(i+1)/2-1]==Nil) // 给叶子赋非空值但父亲节点为空
return ERROR;
else if(value==Nil&&(T[i*2+1]!=Nil||T[i*2+2]!=Nil)) // 给父亲节点赋空值但有叶子(不空)
return ERROR;
T[i]=value;
return OK;
}
// 若e是T的非根结点,则返回它的父亲节点
TElemType Parent(SqBiTree T,TElemType e)
{
int i;
if(T[0]==Nil) // 空树
return Nil;
for(i=1;i<=MAX_TREE_SIZE-1;i++)
if(T[i]==e) // 找到e
return T[(i+1)/2-1];
return Nil; // 没找到e
}
// 返回e的左儿子
TElemType LeftChild(SqBiTree T,TElemType e)
{
int i;
if(T[0]==Nil) // 空树
return Nil;
for(i=0;i<=MAX_TREE_SIZE-1;i++)
if(T[i]==e) // 找到e
return T[i*2+1];
return Nil; // 没找到e
}
// 返回e的右儿子
TElemType RightChild(SqBiTree T,TElemType e)
{
int i;
if(T[0]==Nil) // 空树
return Nil;
for(i=0;i<=MAX_TREE_SIZE-1;i++)
if(T[i]==e) // 找到e
return T[i*2+2];
return Nil; // 没找到e
}
// 返回e的左兄弟
TElemType LeftSibling(SqBiTree T,TElemType e)
{
int i;
if(T[0]==Nil) // 空树
return Nil;
for(i=1;i<=MAX_TREE_SIZE-1;i++)
if(T[i]==e&&i%2==0) //
return T[i-1];
return Nil; // 没找到e
}
// 返回e的右兄弟
TElemType RightSibling(SqBiTree T,TElemType e)
{
int i;
if(T[0]==Nil) // 空树
return Nil;
for(i=1;i<=MAX_TREE_SIZE-1;i++)
if(T[i]==e&&i%2)
return T[i+1];
return Nil; // 没找到e
}
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]);
}
// 后序遍历T
Status PostOrderTraverse(SqBiTree T)
{
if(!BiTreeEmpty(T)) // 树不空
PostTraverse(T,0);
printf("\n");
return OK;
}
// 层序遍历二叉树
void LevelOrderTraverse(SqBiTree T)
{
int i=MAX_TREE_SIZE-1,j;
while(T[i]==Nil)
i--; // 找到最后一个非空结点的序号
for(j=0;j<=i;j++) // 从根结点起,按层序遍历二叉树
if(T[j]!=Nil)
visit(T[j]); // 只遍历非空的结点
printf("\n");
}
// 逐层、按本层序号输出二叉树
void Print(SqBiTree T)
{
int j,k;
Position p;
TElemType e;
for(j=1;j<=BiTreeDepth(T);j++)
{
printf("第%d层: ",j);
for(k=1;k<=powl(2,j-1);k++)
{
p.level=j;
p.order=k;
e=Value(T,p);
if(e!=Nil)
printf("%d:%d ",k,e);
}
printf("\n");
}
}
int main()
{
Status i;
Position p;
TElemType e;
SqBiTree T;
InitBiTree(T);
CreateBiTree(T);
printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
i=Root(T,&e);
if(i)
printf("二叉树的根为:%d\n",e);
else
printf("树空,无根\n");
printf("层序遍历二叉树:\n");
LevelOrderTraverse(T);
printf("前序遍历二叉树:\n");
PreOrderTraverse(T);
printf("中序遍历二叉树:\n");
InOrderTraverse(T);
printf("后序遍历二叉树:\n");
PostOrderTraverse(T);
printf("修改结点的层号3本层序号2。");
p.level=3;
p.order=2;
e=Value(T,p);
printf("待修改结点的原值为%d请输入新值:50 ",e);
e=2020;
Assign(T,p,e);
printf("前序遍历二叉树:\n");
PreOrderTraverse(T);
printf("结点%d的父亲节点为%d,左右儿子分别为",e,Parent(T,e));
printf("%d,%d,左右兄弟分别为",LeftChild(T,e),RightChild(T,e));
printf("%d,%d\n",LeftSibling(T,e),RightSibling(T,e));
ClearBiTree(T);
printf("清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
i=Root(T,&e);
if(i)
printf("二叉树的根为:%d\n",e);
else
printf("树空,无根\n");
return 0;
}
链式存储结构
typedef struct TreeNode *BinTree;
typedef BinTree Position;
struct TreeNode{
ElementType Data;
BinTree Left;
BinTree Right;
}
二叉树的遍历
(1)先序遍历
- 访问根节点
- 先序遍历其左子树
- 先序遍历其右子树
void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
printf("%c",T->data);
PreOrderTraverse(T->lchild); //再先序遍历左子树
PreOrderTraverse(T->rchild); //最后先序遍历右子树
}
先序遍历=> A B D F E C G H I
(2)中序遍历
- 中序遍历其左子树
- 访问根节点
- 中序遍历其右子树
中序遍历=> D B E F A G H C I
(3)后序遍历
-
后序遍历其左子树
-
后序遍历其右子树
-
访问根节点
后序遍历=> D E F B H G I C A
(4)中序遍历非递归算法 -
遇到一个节点,就把他压栈,并去遍历他的左子树
-
当左子树遍历结束后,从栈顶弹出这个节点并访问它
-
然后按其右指针再去中序遍历该节点的右子树
void InOrderTraversal(BinTree BT)
{
BinTree T = BT;
Stack S = CreatStack(MaxSize);
while( T || !IsEmpty(S) ){
while(T){
Push(S,T);
T = T->Left;
}
if(!IsEmpty((S)){
T = Pop(S);
printf("%d",T->Data);
T = T->Right;
}
}
}
(5)层序遍历
核心问题就是二维结构的线性化,可以使用堆栈、队列实现
队列实现:遍历从根节点开始,首先将根节点入队,然后开始执行循环:节点出队、访问该节点、其左右儿子入队
基本过程:
- 从队列中取出一个元素
- 访问该元素所指节点
- 若该元素所指节点的左、右孩子节点为空,则将其左、右孩子的指针顺序入队。
void LevelOrderTraversal(BinTree BT)
{
Queue Q; BinTree T;
if(!BT) return;
Q = CreatQueue(Maxsize);
AddQ(Q,BT);
while (!IsEmptyQ(Q)){
T = DeleteQ(Q);
printf("%d",T->Data);
if (T->Left) AddQ(Q,T->Left);
if (T->Right) AddQ(Q,T->Right);
}
}
链式存储的基本运算
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 // 存储空间初始分配量
typedef int Status;
// 用于构造二叉树
int in=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;
}
}
typedef char TElemType;
TElemType Nil=' '; // 字符型以空格符为空
Status visit(TElemType e)
{
printf("%c ",e);
return OK;
}
typedef struct BiTNode // 结点结构
{
TElemType data; // 结点数据
struct BiTNode *lchild,*rchild; // 左右儿子指针
}BiTNode,*BiTree;
// 构造空二叉树
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
}
}
// 按前序输入二叉树中结点的值(一个字符)
// #表示空树,构造二叉链表表示二叉树T。
void CreateBiTree(BiTree *T)
{
TElemType ch;
// scanf("%c",&ch);
ch=str[in++];
if(ch=='#')
*T=NULL;
else
{
*T=(BiTree)malloc(sizeof(BiTNode));
if(!*T)
exit(OVERFLOW);
(*T)->data=ch; // 生成根结点
CreateBiTree(&(*T)->lchild); // 构造左子树
CreateBiTree(&(*T)->rchild); // 构造右子树
}
}
// 若T为空二叉树,则返回TRUE,否则FALSE
Status BiTreeEmpty(BiTree T)
{
if(T)
return FALSE;
else
return TRUE;
}
#define ClearBiTree DestroyBiTree
// 返回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;
return i>j?i+1:j+1;
}
// 返回T的根
TElemType Root(BiTree T)
{
if(BiTreeEmpty(T))
return Nil;
else
return T->data;
}
// 返回p所指结点的值
TElemType Value(BiTree p)
{
return p->data;
}
// 给p所指结点赋值为value
void Assign(BiTree p,TElemType value)
{
p->data=value;
}
// 前序递归遍历
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);// 显示结点数据,可以更改为其它对结点操作
}
int main()
{
int i;
BiTree T;
TElemType e1;
InitBiTree(&T);
StrAssign(str,"ABDH#K###E##CFI###G#J##");
CreateBiTree(&T);
printf("构造空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
e1=Root(T);
printf("二叉树的根为: %c\n",e1);
printf("\n前序遍历二叉树:");
PreOrderTraverse(T);
printf("\n中序遍历二叉树:");
InOrderTraverse(T);
printf("\n后序遍历二叉树:");
PostOrderTraverse(T);
ClearBiTree(&T);
printf("\n清除二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",BiTreeEmpty(T),BiTreeDepth(T));
i=Root(T);
if(!i)
printf("树空,无根\n");
return 0;
}