第五章 树和二叉树(5.4-5.5.1)

5.4 二叉树的性质和存储结构

5.4.1二叉树的性质

性质1:第i层上至多有2^{i-1}个结点(i从1开始增加)

性质2:深度为 l 的二叉树至多有2^{l}-1个结点

二叉树的深度为l,每一层上的结点数目为2^{i-1},由等比数列的求和公式可以得出结果:

性质3:对于任何一棵二叉树,若叶子结点数为N0,度为2的结点数为N2,则

由树的基本定义与结构中可以了解得叶子结点数与度的概念,假设二叉树中的结点总数为N,同时度为1的结点数为N1,有以下等式:

​ ![](F:\极客班\数据结构-极客班\第五章 树和二叉树\image\bbfgv.gif)

根据度的概念可以得知,结点向下延伸的直线连接至另一个结点,向下关联的结点数目就是结点的度,而每一个结点仅仅只有一条由上向下连接的直线。所以,所有的结点数目之和等于连接的直线数加1(1代表顶端的根结点),度为2的点向下发出的直线数目为2,度为1的结点发出的数目为1:

N=2*N2+N1+1

综合以上两式,得出结论为: N0=N2+1

注:完全二叉树与满二叉树的区别,满二叉树是指深度为l,并且具有2^{l}-1个结点的二叉树,完全二叉树是指在深度为l, 结点数为n的二叉树中,当且仅当每一个结点与深度为l的满二叉树中编号为1至n的结点一一对应时,该二叉树称作完全二叉树(即二叉树若存在第l层,其上l-1层必定是满二叉树)。两种二叉树的示意图如下:

性质4:具有n个结点的完全二叉树的深度k为

\left \lfloor log _{2}n\right \rfloor+1

假设完全二叉树的深度为k,又因为完全二叉树的性质,导致其节点数目n处于2{k-1}-1至2-1之间,得出![](F:\极客班\数据结构-极客班\第五章 树和二叉树\image\Niubi.gif)由于k是整数的缘故,所以

k=\left \lfloor log_{2} n\right \rfloor+1

性质5: 若对一棵有n个结点的完全二叉树结点按每一层从左到右的顺序来编号,对于任何一个编号为i的结点均有以下结论:

(1) i = 1,则 i 是二叉树的根,无双亲。若i >1,则双亲是编号为 的结点

(2)若 2i> n,结点i无左孩子,否则,其左孩子结点的编号为2i

(3)若2i+1>n,则结点i 无右孩子,否则其右孩子是节点2i+1

以图“完全二叉树”为例,编号为3的结点无右孩子,编号为4、5、6的结点无左孩子

5.4.2二叉树的存储结构

1.顺序存储结构

#define MAXTSIZE 100  //二叉树的最大结点数
typedef TElemType SqBiTree[MAXTSIZE];//0号单元存储根结点
SqBiTree bt; //数组bt

2.链式存储结构

//二叉树的二叉链表存储表示
typedef struct BiTNode{
	TElemType data;  //结点数据域
	struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree;

5.5遍历二叉树和线索二叉树

5.5.1遍历二叉树

1.遍历二叉树的算法描述

1.1先序遍历

若二叉树为空,则:空操作
否则:
访问根结点(D);
先序遍历左子树(L);
先序遍历右子树(R);

void PreOrderTraverse(BiTree T)
{
	if (T)  //非空二叉树
	{
		printf("%d", T->data);  //访问根结点
		PreOrderTraverse(T->lchild); //递归遍历左子树
		PreOrderTraverse(T->rchild);  //递归遍历右子树
	}
}

1.2.中序遍历

若二叉树为空,则:空操作
否则:

中序遍历左子树(L);
访问根结点(D);
中序遍历右子树(R);

void InOrderTraverse(BiTree T)
{
	if (T)  //非空二叉树
	{
		InOrderTraverse(T->lchild); //递归遍历左子树
		printf("%d", T->data);  //访问根结点
		InOrderTraverse(T->rchild);  //递归遍历右子树
	}
}
算法5.1 中序遍历的递归算法
void InOrder(BiTree T){
	if(T){
		InOrder(T->lchild);
		cout<<T->data<<" ";
		InOrder(T->rchild);
	}
}
算法5.2 中序遍历的非递归算法
void InOrder(BiTree T){
	//还是模拟上面的遍历过程
	BiTree ptr[20];
	int top = -1;
/*下面的双重判断和前面的一样,在进栈、出栈的过程中可能会出现栈空的情况,而此时的遍历还没有结束,
所以需要据此来维持循环的进行。*/
	while(T || top!=-1){
		while(T){
			ptr[++top] = T;
			T = T->lchild;
		}
		if(top!=-1){
			T = ptr[top--];
			cout<<T->data<<" ";   //输出在出栈后
			T = T->rchild;
		} 
	} 
	
}

1.3.后序遍历

若二叉树为空,则:空操作
否则:

后序遍历左子树(L);
后序遍历右子树(R);
访问根结点(D);

void PostOrderTraverse(BiTree T)
{
	if (T)  //非空二叉树
	{
		PostOrderTraverse(T->lchild); //递归遍历左子树
		PostOrderTraverse(T->rchild);  //递归遍历右子树
		printf("%d", T->data);  //访问根结点
	}
}

2.根据遍历序列确定二叉树

3.二叉树遍历的顺序建立二叉链表

posted @     阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示