第五章:树和二叉树

文章目录

第五章:树和二叉树

5.1:树和二叉树的定义

在这里插入图片描述
在这里插入图片描述

5.1.1:树的定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.1.2:树的基本术语

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.1.3:二叉树的定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5.2:案例引入

在这里插入图片描述
在这里插入图片描述

5.3:数和二叉树的抽象数据类型定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

5.4.1:二叉树的性质

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5.4.1.1:两种特殊形式的二叉树

在这里插入图片描述

5.4.1.1.1:满二叉树

在这里插入图片描述
在这里插入图片描述

5.4.1.1.2:完全二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.4.2:二叉树的存储结构

5.4.2.1:顺序存储结构

在这里插入图片描述

//顺序存储结构
#define MAXTSIZE	100					//二叉数的最大结点
typedef TELemType SqBiTree[MAXTSIZE];	//0号单元存储跟结点
SqBiTree bt;
5.4.2.2:链式存储结构

在这里插入图片描述

//二叉数的链式存储结构
typedef struct BiTNode{
	TElemType data;						//结点数据域
	struct BiTNode *lchild,*rchild;		//左右孩子结点
}BiNode,*BiTree;
5.4.2.3:三叉链表

在这里插入图片描述

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

5.5.1:遍历二叉数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.5.1.1:遍历算法的实现
5.5.1.1.1:先序遍历

在这里插入图片描述

//二叉数先序遍历算法
Status PreOrderTraver(BiTree T)
{
	if(T==NULL) return OK;				//空二叉数		
	else 
	{
		vist(T);						//访问根结点 例如:输出根结点printf("%d\t",T->data);	
		PreOrderTraverse(T->lchlid);	//递归遍历左子树
		PreOrderTraverse(T->rchlid);	//递归遍历右子树
	}
}
5.5.1.1.2:中序遍历

在这里插入图片描述

//二叉数中序遍历算法
Status PreOrderTraver(BiTree T)
{
	if(T==NULL) return OK;				//空二叉数		
	else 
	{
		PreOrderTraverse(T->lchlid);	//递归遍历左子树
		vist(T);						//访问根结点 例如:输出根结点printf("%d\t",T->data);	
		PreOrderTraverse(T->rchlid);	//递归遍历右子树
	}
}
//算法5.1:中序遍历的递归算法
void InOrderTraverse(BiTree T)
{//中序遍历二叉数T的递归算法
	if(T)								//若二叉数非空
	{								
		InOrderTraverse(T->lchild);		//中序遍历左子树
		count<<T->data;					//访问根结点
		InOrderTraverse(T->rchild);		//中序遍历右子树
	}
}
//算法5.1:中序遍历的递归算法
void InOrderTraverse(BiTree T)
{//中序遍历二叉数T的递归算法
	if(T)								//若二叉数非空
	{								
		InOrderTraverse(T->lchild);		//中序遍历左子树
		count<<T->data;					//访问根结点
		InOrderTraverse(T->rchild);		//中序遍历右子树
	}
}
5.5.1.1.3:后序遍历

在这里插入图片描述

//二叉数后序遍历算法
Status PreOrderTraver(BiTree T)
{
	if(T==NULL) return OK;				//空二叉数		
	else 
	{
		PreOrderTraverse(T->lchlid);	//递归遍历左子树	
		PreOrderTraverse(T->rchlid);	//递归遍历右子树
		vist(T);						//访问根结点 例如:输出根结点printf("%d\t",T->data);
	}
}
5.5.1.1.4:中序非递归算法

在这里插入图片描述

//算法5.2:中序遍历的非递归算法
void InOrderTraverse(BiTree	T)
{//中序遍历二叉数T的非递归算法
	InitStack(S);	p=T;
	q=new BiTNode;
	while(p||!StackEmpty(S))
	{
		if(p)					//p非空
		{
			Push(S,p);			//根指针进栈
			p=p->lchild;		//根指针进栈,遍历左子树
		}
		else					//p为空
		{
				Pop(S,p);		//退栈
				count<<q->data;	//访问根结点
				p=q->rchild;	//遍历右子树
		}
	}							//while
}
5.5.1.1.5:层次遍历算法

在这里插入图片描述


//使用队形类型定义如下
typedef struct
{
	BTNode data[MaxSize];	//存放对中元素
	int front,rear;			//队头和队尾指针
}SqQueue;					//顺序循环队列类型


//二叉数层次遍历算法
void LevelOrder(BTNode *b)
{
	BTNode *p;	SqQueue *qu;
	InitQueue(qu);				//初始化队列
	enQueue(qu,b);				//根节点指针进入队列
	while(!QueueEmpty(qu))		//队不为空,则循环
	{
		deQueue(qu,p);			//出队结点p
		printf("%c",p->data);	//访问结点p
		if(p->lchild!=NULL)	enQueue(qu,p->lchild);		//有左孩子时将其进队
		if(p->rchild!=NULL)	enQueue(qu,p->rchild);		//有右孩子时将其进队
	}
}
5.5.1.2:遍历算法的分析

在这里插入图片描述

5.5.1.3:二叉树的遍历算法应用
5.5.1.3.1:建立二叉树算法

在这里插入图片描述

//算法5.3:先序遍历的顺序建立二叉链表
void CreateBiTree(BiTree &T)
{//按先序次序输入二叉数中结点的值(一个字符),创建二叉链表表示的二叉树T
	cin>>cn;
	if(ch=='#') T=NULL;				//递归结束
	else							//递归创建二叉树
	{
		T=new BiTNode;				//生成根节点
		T->data=ch;					//根节点数据域置为ch
		CreateBiTree(T->lchild);	//递归创建左子树
		CreateBiTree(T->rchild);	//递归创建右子树
	}								//else
}
5.5.1.3.2:复制二叉树算法

在这里插入图片描述

//算法5.4 复制二叉树
void Copy(BiTree T,BiTree &NewT)
{//复制一颗和T完全相同的二叉树
	if(T=NULL)							//如果是空树,递归结束
	{			
		NewT=NULL;						
		return;
	}
	else
	{
		NewT=new BiTNode;
		NewT->data=T->data;				//复制根结点
		Copy(T->lchild,NewT->lchild);	//递归复制左子树
		Copy(T->rchild,NewT->rchild);	//递归复制右子树
	}									//else
}
5.5.1.3.3:计算二叉树深度算法

在这里插入图片描述

//算法5.5 计算二叉树的深度
int Depth(BiTree T)
{//计算二叉树的深度
	if(T=NULL) return 0;		//如果是空树,深度为0,递归结束
	else
	{
		m=Depth(T->lchild);		//递归计算左子树深度记为m
		n=Depth(T->rchild);		//递归计算右子树慎独记为n
		if(m>n)	return(m+1);	//二叉树的深度为m和n的较大者加1
		else return(n+1);
	}
}
5.5.1.3.4:计算二叉树叶子结点数算法

在这里插入图片描述

//算法5.6 统计二叉数中结点的个数
int NodeCount(BiTree T)
{//统计二叉数T中结点的个数
	if(T=NULL) return 0;				//如果是空树,则结点个数为0,递归结束
	else return NoCount(T->lchild)+NoCount(T->rchild)+1;
	//否则结点个数为左子树的结点个数+右子树的结点个数加1
}

5.5.2:线索二叉数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


//二叉数的二叉线索存储表示
typedef struct BiThrNode
{
	TEleTypem data;
	struct BIThrNode *lchild,*rchild;		//左右孩子指针
	int LTag,RTag;							//左右标志
}BithrNode,*BuThrTree;
5.5.2.1:先序二叉数

在这里插入图片描述

5.5.2.2:中序二叉数

在这里插入图片描述

//算法5.7:以结点p为根的子树中序线索化
void InThreading(BiThrTree p)
{//pre是全局变量,初始化时其右孩子指针为空,便于在数的最左点开始建线索
	if(p)
	{
		InThreading(p->lchild);			//左子树递归线索化
		if(!p->lchild)					//p的左孩子为空
		{
			p->LTag=1;					//给p加上左线索
			p->lchild=pre;				//p的左孩子指针指向pre(前驱)
		}								//if
		else p->LTag=0;					//pre的右孩子为空
		if(!pre->rchild)				
		{
			pre->RTag=1;				//给pre加上右线索
			pre->rchild=p;				//pre的右孩子指针指向p(后驱)
		}								//if
		else pre->RTag=0;				//保持pre指向p的前驱
		pre=p;							//右子树递归线索化
		InThreading(p->rchild);
	}
}
//算法5.8 带头结点的二叉树中序线索化
void InOrderTreading(BiThrTree &Thrt,BiThrTree T)
{//中序遍历二叉数T,并将其中序线索化,Thrt指向头结点
	Thrr=new BiTrNode;				//建立头结点
	Thrt->LTag=0;					//头结点有左孩子,若树非空,则其左孩子为树根
	Thrt->RTag=1;					//头结点的右孩子指针为右线索
	Thrt->rchild=Thrt;				//初始化时右指针指向自己
	if(!T)	Thrt->lchild=Thrt;		//若树为空,则左指针也指向自己
	else
	{
		Thrt->lchild=T; pre=Thrt;	//头结点的左子树指向根,pre初值指向头结点
		InTreading(T);				/*用算法5.7(以结点p为根的子树中序线索化),
									对以T为根的二叉树进行中序线索化*//
		pre->rchild=Thrt;			//算法5.7结束后,pre为最右结点,pre的右线索指向头结点

		pre->RTag=1;
		Thrt->rchild=pre;			//头结点的右线索指向pre
	}
}
//算法5.9 遍历中序线索二叉数
void InOrderTraverse_Thr(BiThrTree T)
{//T指向头结点,头结点的左链lchild指向根结点
//中序遍历二叉树线索树T的非递归算法,对每个数据元素直接输出
	p=T->lchild;						//p指向根结点
	while(p!=T)							//空树或遍历结束时,p==T
	{
		while(p->LTag==0)	p=p->lchild;	//沿左孩子向下
		count<<p->data;						//访问其左子树为空的结点
		while(p->RTag==1&&p->rchild!=T)
		{
			p=p->rchild; count<<p->data;	//沿右线索访问后继结点
		}
		p=p->rchild;						//转向p的右子树
	}
}
5.5.2.3:后序二叉树

在这里插入图片描述

5.5.2.4:练习

在这里插入图片描述

在这里插入图片描述

5.6:树和森林

在这里插入图片描述

5.6.1:树的存储结构

5.6.1.1:双亲表示法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


//C语言类型描述:
typedef struct PTNode
{
	TElemType data;
	int parent;		//双亲位置域
}PTNode;

//树结构
#define MAX_TREE_SIZE 100;
typedef struct
{
	PTNode nodes[MAX_TREE_SIZE];
	int r,n;//跟结点的位置和结点个数
}PTree;
5.6.1.2:孩子链表

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

//孩子结点结构
typedef struct CTNode
{
	int child;
	struct CTNode *next;
}*Child;

//双亲结点结构
typedef struct
{
	TElemType data;
	ChildPtr firstchild; //孩子链表指针
}CTBox;
//树结构
typedef struct 
{
	CTBox nodes[MAX_TREE_SIZE];
	int n,r;//结点数和跟结点的位置
}CTree;

5.6.1.3:孩子兄弟表示法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

//孩子兄弟法
typedef struct CSNode
{
	ElemType data;
	struct SCNode *fristchild,*nextsibling;
}CSNode,*CSTree;

5.6.2:森林和二叉树的转换

5.6.2.1:树和二叉树的转换

在这里插入图片描述

5.6.2.2:森林和二叉树的转换

在这里插入图片描述

5.6.3:树和森林的遍历

在这里插入图片描述

5.7:哈夫曼树及其应用

5.7.1:哈夫曼树的基本概念

在这里插入图片描述
在这里插入图片描述

5.7.2:哈夫曼树的构造算法

在这里插入图片描述
在这里插入图片描述

//算法 5.10:构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT,int n)
{//构造哈夫曼树
	if(n<=1)	return;
	m=2*n-1;
	HT=new HTNode[m+1];			//0好单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点
	for(i=1;i<m;i++)			//将1-m号单元中的双亲,左孩子,右孩子的下标都初始化为0
	{
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	for(i=1;i<=n;i++)			//输入前n个单元中叶子结点的权值
		cin>>HT[i].weight;

	/*初始化工作结束,下面创建哈夫曼树*/
	for(i=n+1;i<=m;i++)
	{//通过n-次的选择,删除,合并来创建哈夫曼树
		Select(HT,i-1;s1,s2);			
		//在HT[K](0<=k<=k-1)中选择两个其双亲域为0其权值最小的结点,并返回它们在HT中的序号s1和s2
		HT[s1].parent=i;HT[s2].parent=i;
		//得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为i
		HT[i].lchild=s1;HT[i].rchild=s2;			//s1s2分别作为i的左右孩子
		HT[i].weight=HT[s1].weight+HT[s2].weight;	//i的权值为左右孩子权值之和
	}
}

5.7.3:哈夫曼编码

5.7.3.1:哈夫曼编码思想

在这里插入图片描述
在这里插入图片描述

5.7.3.2:哈夫曼编码的算法实现

在这里插入图片描述

//算法 5.11根据哈夫曼树求哈夫曼编码
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
	HC=new char*[n+];
	cd=new char[n];
	cd[n-1]='\0';
	for(i=14;i<=n;i++)
	{
		start=n-1;
		c=i;f=HT[i].parent;
		while(!f=0)
		{
			--start;
			if(HT[f].lchild==c)	cd[start]='0';
			else cd[start]='1';
			c=f;f=HT[f],parent;
		}
		HC[i]=new char[n-start];
		strcpy(HC[i],&cd[start]);
	}
	delete cd;
}
5.7.3.3:文件的编码和译码

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 请问如何判断一个二叉树是完全二叉树? 答:完全二叉树是指除了最后一层外,其他每一层都是满的,并且最后一层的节点从左到右连续地填满,缺失的节点只能在最后一层的最右侧。因此,判断一个二叉树是否为完全二叉树的方法如下: - 如果二叉树为空,则它是完全二叉树; - 如果二叉树不为空,则按照层次遍历的顺序,从上到下、从左到右遍历每个节点,并进行以下判断: - 如果当前节点有右子树,但没有左子树,则它不是完全二叉树; - 如果当前节点不是满的(即左子树或右子树为空),那么它之后的所有节点都必须是叶子节点,否则它不是完全二叉树; - 如果按照层次遍历的顺序遍历完所有节点都满足上述条件,则它是完全二叉树。 2. 请问如何求一棵二叉树的深度? 答:二叉树的深度是指从根节点到最深叶子节点的路径长度。求一棵二叉树的深度可以使用递归的方法: - 如果二叉树为空,则深度为0; - 如果二叉树不为空,则深度为1加上左右子树中的最大深度。具体而言,可以先递归求解左子树的深度和右子树的深度,然后取它们的最大值并加1即可。 下面是求解二叉树深度的 Python 代码示例: ```python def depth(root): if root is None: return 0 else: left_depth = depth(root.left) right_depth = depth(root.right) return max(left_depth, right_depth) + 1 ``` 3. 请问如何对一棵二叉树进行前序遍历? 答:前序遍历是指先访问根节点,然后按照先左后右的顺序递归遍历左右子树。对于一棵二叉树,可以使用递归或者非递归的方法进行前序遍历。 - 递归方法:如果二叉树为空,则直接返回;否则,先访问根节点,然后递归遍历左子树和右子树。 ```python def preorder_recursive(root): if root is None: return print(root.val) preorder_recursive(root.left) preorder_recursive(root.right) ``` - 非递归方法:使用栈来模拟递归的过程。首先将根节点压入栈中,然后每次从栈中弹出一个节点,访问它,并将它的右子节点和左子节点依次压入栈中。 ```python def preorder_iterative(root): if root is None: return stack = [root] while stack: node = stack.pop() print(node.val) if node.right: stack.append(node.right) if node.left: stack.append(node.left) ``` 4. 请问如何对一棵二叉树进行中序遍历? 答:中序遍历是指按照先左后根再右的顺序递归遍历左子树、访问根节点、递归遍历右子树。对于一棵二叉树,可以使用递归或者非递归的方法进行中序遍历。 - 递归方法:如果二叉树为空,则直接返回;否则,先递归遍历左子树,然后访问根节点,最后递归遍历右子树。 ```python def inorder_recursive(root): if root is None: return inorder_recursive(root.left) print(root.val) inorder_recursive(root.right) ``` - 非递归方法:使用栈来模拟递归的过程。首先将根节点入栈,并将它的所有左子节点依次入栈,直到没有左子节点。然后每次从栈中弹出一个节点,访问它,并将它的右子节点入栈,并将右子节点的所有左子节点依次入栈。 ```python def inorder_iterative(root): if root is None: return stack = [] node = root while stack or node: while node: stack.append(node) node = node.left node = stack.pop() print(node.val) node = node.right ``` 5. 请问如何对一棵二叉树进行后序遍历? 答:后序遍历是指按照先左后右再根的顺序递归遍历左右子树、访问根节点。对于一棵二叉树,可以使用递归或者非递归的方法进行后序遍历。 - 递归方法:如果二叉树为空,则直接返回;否则,先递归遍历左子树和右子树,最后访问根节点。 ```python def postorder_recursive(root): if root is None: return postorder_recursive(root.left) postorder_recursive(root.right) print(root.val) ``` - 非递归方法:使用栈来模拟递归的过程。首先将根节点入栈,然后每次从栈中弹出一个节点,将它的左子节点和右子节点依次入栈。在处理完所有子节点之后,访问弹出的节点。 ```python def postorder_iterative(root): if root is None: return stack1 = [root] stack2 = [] while stack1: node = stack1.pop() stack2.append(node) if node.left: stack1.append(node.left) if node.right: stack1.append(node.right) while stack2: node = stack2.pop() print(node.val) ``` 注意,在非递归方法中,需要使用两个栈来实现后序遍历。第一个栈用于存储遍历的节点,第二个栈用于存储最终的遍历结果。首先将根节点入第一个栈,然后每次从第一个栈中弹出一个节点,并将它入第二个栈,同时将它的左子节点和右子节点依次入第一个栈。在第一个栈为空时,第二个栈中存储的节点顺序就是后序遍历的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值