第七章 树

引言

  1. 文章大纲:本文由知识点思维导图,注意事项与易错点,题型总结,方法心得四部分组成。
  2. 思维导图中,标红的是重点内容,标黄的是次重点。
  3. 本章实验点此查看。
  4. 码字不易,如果这篇文章对您有帮助的话,希望您能点赞、评论、收藏,投币、转发、关注。您的鼓励就是我前进的动力!

知识点思维导图

在这里插入图片描述
(点此查看原图)

补充:

  1. 二叉树的性质:
    1)二叉树的第 i 层最多有 2 i − 1 2^{i-1} 2i1 个结点。
    2)深度为k的二叉树至多有 2 k − 1 2^k-1 2k1个结点(k≥1)。
    3)对任何一棵二叉树,若它含有 n 0 n_0 n0个叶子结点、 n 2 n_2 n2个度为2的结点,则必存在关系式: n 0 = n 2 + 1 n_0 = n_2+1 n0=n2+1
    4)具有n个结点的完全二叉树的深度为 「 l o g 2 n 」 + 1 「log_2n」+1 log2n+1,「X」表示不大于X的最大整数。
    5)若对含n个结点的完全二叉树从上到下且从左至右进行1至n的编号,则对完全二叉树中任意一个编号为i的结点:
    (1)若 i=1,则该结点是二叉树的根,无双亲,否则,编号为「 i/2」 的结点为其双亲结点;
    (2)若 2i>n,则该结点无左孩子,否则,编号为2i的结点为其左孩子结点;
    (3)若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。

注意事项与易错点

NULL

题型总结

一、二叉树的性质

  1. 性质:见补充部分。
  2. 例题: 已知一棵完全二叉树的第6层(设根为第1层)有8个叶子结点,则该完全二叉树的结点个数最多是( )。
    A. 39  B. 52  C. 111  D. 119

    解析:
    完全二叉树的叶子结点只能在最下两层,对于本题,结点最多的情况是第6层为倒数第二层,即1~6层构成一个满二叉树,其结点总数为26-1=63。
    其中第6层有 2 5 = 32 2^5=32 25=32个结点,含8个叶子结点,则另外有32-8=24个非叶子结点,它们中每个结点有两个孩子结点(均为第7层的叶子结点),计48个叶子结点。这样最多的结点个数=63+48=111。
    故答案为C。

二、推导二叉树的遍历结果

  1. 常用解题规律:后序的最后一个是根结点;前序的第一个是根结点。(此规律适用于找出根结点和所有层次的内部结点至叶结点)
  2. 解题步骤:
    1)用规律找出根结点,将序列分为两棵子树;
    2)在两棵子树中用规律,找子树的“根结点”;
    3)再根据中序遍历与前/后序遍历判断剩下元素的位置。
  3. 例题:已知二叉树的先序为:ABCDEFGHIJ,中序为:CDBFEAIHGJ,试求相应的后序。
    解析:
    根据解题步骤画出相应的二叉树,在对二叉树进行后序遍历即可。
    可得后序为:DCFEBIHJGA。

三、树、森林与二叉树的转换

  1. 原理与操作步骤:见思维导图。
  2. 详解

四、构造哈夫曼树

  1. 原理:见思维导图。
  2. 补充:利用赫夫曼树可以构造一种不等长的二进制编码,并且构造所得的赫夫曼编码是一种最优前缀编码,即使所传电文的编码总长度最短。
  3. 详解

五、重要代码

  • 二叉链表存储结构
typedef struct BiTNode { 
	TElemType      data; 		     //数据域
	struct BiTNode  *lchild, *rchild; //左右孩子指针
}BiTNode, *BiTree;
  • 前序遍历算法
void PreOrder(BiTree T){
 if (T!=NULL) { 
  visit(T->data);    //访问数据域,可改为其他操作。
  PreOrder(T->lchild);
  PreOrder(T->rchild);
 }
}

1)中序、后序算法只需将前序遍历算法中打印操作调到递归调用语句中间或之后即可。

  • 前序构造二叉树
Status CreatePreBiTree(BiTree &T){	
	char ch;   
	ch=getchar( );
	if (ch == '#')
		T=NULL;
	else{
		T = (BiTree)malloc(sizeof(BiTNode));
		T->data = ch;
		CreatePreBiTree(T->lchild);
		CreatePreBiTree(T->rchild);
	}
	return OK;
}

1)上述代码输入时,按先序次序输入,以"#"表示空树。
2)构造二叉树相当于将遍历算法中打印结点语句改为了分配存储空间并给结点赋值的操作。

  • 线索二叉树
//线索二叉树存储结构
typedef struct BiThrNode
{
	ElemType data;
	struct BiThrNode *lchild,*rchild;
	int LTag, RTag;  //左右标志
}BiThrNode,*BiThrTree; 

//中序遍历二叉树T,并将其中序线索化,head指向头结点 
int InOrderThr(BiThrTree head, BiThrTree T){
	if ((head=(BiThrTree*)malloc(sizeof(BiThrNode)))==0)
		 return 0;   //动态分配内存,若分配失败返回0
	head->ltag=0;  
	head->rtag=1;  
	head->rchild=head;
	if (T==NULL) 
		head->lchild = head;
	else {
		head->lchild=T;  
		pre=head;
		InThreading(T);
		pre->rchild=head;  
		pre->rtag=1;//最后一个节点线索化
		head->rchild=pre;
	}
	return 1;
}

//中序遍历进行中序线索化 
void InThreading(BiThrTree p){
	if (p){
		InThreading(p->lchild); 	//左子树线索化
		
		if (p->lchild==0){
			p->ltag=1;  
			p->lchild=pre;
		} //前驱线索化		
		if (pre->rchild==0){
			pre->rtag=1; 
			pre->rchild=p;
		}	//后继线索化	
		pre=p; 			//使pre指向p的前驱
		
		InThreading(p->rchild);	//右子树线索化
	}
}

1)每个结点线索化操作包括:若左子树为空,左标志置1,左指针指向其前驱;若右子树为空,右标志置1,右指针指向其后继。
2)进行线索化时,应知道每个结点的前驱和后继结点的地址。对于前驱结点的指针值,可设一指针变量pre来记录。

  • 线索二叉树的查找
//中序线索二叉树中查找任意结点的前驱结点
BiThrTree InPreNode(BiThrTree p){
	BiThrTree pre;
	pre=p->lchild;
	if (p->ltag!=1){
		while(pre->rtag==0)
			pre=pre->rchild;
	}
	return(pre);
} 

//中序线索二叉树中查找任意结点的后继结点
BiThrTree InPostNode(BiThrTree p){
	BiThrTree post;
	post=p->rchild;
	if (p->rtag!=1){
		while(post->ltag==0)
			post=post->lchild;
	}
	return(post);
}

//中序线索二叉树上查找值为x的结点 
BiThrTree Search(BiThrTree head,ElemType x) {
	BiThrTree p;
	p=head->lchild;//root
	while(p->ltag==0&&p!=head)
		p=p->lchild;//第一个节点
	while(p!=head && p->data!=x) 
		p=InPostNode(p);
	if (p==head) {
		printf("没找到!\n");
		return 0; 
	}
	else 
		return p;
}

方法心得

NULL

参考资料:
[1] 程杰. 大话数据结构. 北京:清华大学出版社, 2020.
[2]严蔚敏,吴伟民. 数据结构 (C语言版). 北京:清华大学出版社, 1997.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值