二叉树的重要性质
二叉树是一种非线性数据结构,他是一个有穷的结点的集合。该集合可为空或非空。一棵二叉树由由他的根节点和称为其左子树和右子树的两个不相交的二叉树组成。(因此,每个二叉树最多有2个结点,且子节点有左右之分)。
二叉树重要性质
1) 一个二叉树第 i 层的最大结点数为 2^( i -1),i >=1;
2) 深度为 k 的二叉树有最大结点总数 2^k -1 ,k>=1;
3) 对任何非空二叉树,若 n0表示叶结点个数,n2表示度为 2 的非叶结点个数,则二者满足 n0=n2 +1 ;
(二叉树的性质还有很多,暂不一 一列举了)
二叉树的存储
二叉树的存储
二叉树可用数组和链表存储,最常用的方式为链表
二叉树的链表结构
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data; //结点数据 ElementType可为任何数据类型
BinTree Left; //指向左子树
BinTree Right; //指向右子树
};
二叉树的操作
先序遍历生成二叉树
/* 由于涉及给 T 分配存储空间和给 T 赋值,故传入指向 T 的指针 */
void CreateBinTree ( BinTree *T){
ElementType num; //假设 ElementType 为int 型
scanf("%d",&num);
if(num==0) //num等于 0 表示空树
*T=NULL;
else{
*T=(BinTree)malloc(sizeof(BinTree));
if(!(*T))
exit(1); //分配内存失败,退出函数
*T->Data=num;
CreateBinTree(&(*T)->Left); //递归构造左子树
CreateBinTree(&(*T)->Right); //递归构造右子树
}
}
(也可借助队列层序生成二叉树,但比起递归生成二叉树,层序生成稍显复杂,以后再补充吧)
二叉树的遍历
二叉树的遍历
二叉树的非递归遍历方法会用到堆栈相关知识,如果对堆栈还不是很了解可以参考博主的这篇博文 堆栈相关操作
先序遍历
/* 递归算法 */
void PreorderTraversal (BinTree BT){
if(BT){ //若树非空
printf("%d",BT->Data);
PreorderTraversal(BT->Left); //递归遍历左子树
PreorderTraversal(BT->Right); //递归遍历右子树
}
}
/* 非递归算法,借助堆栈实现 即把每一个走过的结点入栈 */
/* 不好理解,建议自己画个二叉树按程序走一遍 */
void PreorderTraversal (BinTree BT){ //传入二叉树的头结点
BinTree T=BT;
Stack S=CreateStack (MaxSize); //新建一个堆栈
while(T || !IsEmpty(S)){ //若树非空或堆栈非空
while(T){ //若树非空
Push(S,T) //将树的头结点入栈
printf("%d",T->Data); //输出头结点的值,在此假设值为整型
T=T->Left; //循环判断其左子树
}
if(!IsEmpty (S)){ //若堆栈非空
T=Pop(S); //取出一个栈顶元素并赋值给 T
T=T->Right; //循环判断其右子树
}
}
}
中序遍历
/* 递归算法 */
void InorderTraversal (BinTree BT){
if(BT){
InorderTraversal(BT->Left); //先递归遍历左子树
printf("%d",BT->Data);
InorderTraversal(BT->Right); //后递归遍历右子树
}
}
/* 非递归算法 */
/* 与先序遍历的非递归算法相比只是 printf 函数的位置移动了 */
void PreorderTraversal (BinTree BT){
BinTree T=BT;
Stack S=CreateStack (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;
}
}
}
后序遍历
/* 递归算法 */
void PostorderTraversal (BinTree BT){
if(BT){
PostorderTraversal (BT->Left);
PostorderTraversal (BT->Right);
printf("%d",BT->Data);
}
}
/* 非递归算法 (与先序和中序遍历不同,在此需要多加一个选择的步骤)*/
void PreorderTraversal (BinTree BT){
BinTree T=BT, P=NULL; //P 在此暂时指向已输出的结点
Stack S=CreateStack (MaxSize);
while(T || !IsEmpty (S));{
/* 该处与先序和中序遍历一致,将根和左子树依次入队 */
while(T){
Push (S, T);
T=T->Left;
}
if(!IsEmpty(S)){
T=Pop(S);
/*若该结点的右子树为空或已输出,则输出该结点,否则将其右子树入队 */
if(T->Right==P || T->Right==NULL){
printf("%d",T->Data);
P=T;
T=NULL;
}else{
Push(S, T);
T=T->Right;
}
}
}
}
前序、中序、后续遍历的递归算法只是输出函数的位置不同,以最简单的只有一个父结点和他的左右子结点为例:
先序遍历 先输出父结点再依次输出左右子结点;
中序遍历 先输出左子结点,再输出父结点最后输出右子结点
后序遍历 先输出左子结点,再输出右子结点最后输出父结点
层序遍历
/* 与前面借助堆栈不同,在此层序遍历借助队列实现 */
void LevelorderTraversal (BinTree BT){
Queue Q;
BinTree T;
if(!BT) return ;//若树为空,直接结束函数,不需返回任何值
Q=CreateQueue( ); //创建一个队列Q
AddQ (Q, BT); //将树的根节点入队
while(! IsEmpty(Q)){ //若队列非空,进入循环
T=DeleteQ(Q); //取出对头元素并输出
printf("%d", T->data);
/* 若取出结点的子树非空,依次将所取出的结点的非空子结点入队*/
if(T->Left) AddQ(Q, T->Left);
if(T->Right) AddQ(Q, T->Right);
}
}
输出叶结点
二叉树叶结点输出算法
/* 同样利用递归的方法 */
void PreorderPrintLeaves (BinTree BT){
if(BT){
if(!BT->Left && !BT->Right) //若结点没有子结点即为叶结点 ,输出
printf("%d",BT->Data);
PreorderPrintLeaves(BT->Left); //递归判断左子树
PreorderPrintLeaves(BT->Right); //递归判断右子树
}
}
计算树高
求二叉树高度算法
int GetHeight (BinTree BT){
int HL,HR,MaxH;
if(BT){
HL=GetHeight(BT->Left); //递归遍历左子树
HR=GetHeight(BT->Right); //递归遍历左子树
/* 比较左右子树的高度,取较大的值 */
MaxH=HL>HR?HL:HR;
return (MaxH+1); //最后 +1 表示加上根节点 ,MaxH+1 即为最终树高
}
else return 0; //终止条件是当树为空时,返回 0
}
关于二叉树的操作暂时就先整理这么多,以后还会继续补充,由于本人也正在学习当中,所以有错误的地方希望大家不吝指正。