【考研--数据结构】 一对多数据结构——树与森林

注:以下内容均为个人复习使用,不保证所述内容绝对严谨;
注:考研知识点相对基础,因此这里只做知识点合集,不保证内容详细。

一、基本术语:

根节点 root,双亲节点: fa[pos] ; 子节点 to[pos]
兄弟节点:父节点相同的节点
节点的度:子树的个数 (向下的出度)
树的度:节点的度的max
叶子节点:度为0的点
内部节点:非root的点
深度:depth。 depth[root]=1;
森林:多个树的集合

三种存储方法:
孩子链表表示法(即用指针写的邻接表)
孩子兄弟表示法(指针域存储:第一个孩子,和第一个兄弟)
双亲表示法(使用数组,存fa[pos])

二、二叉树

定义:度至多为2,分左右子树的树

1. 完全二叉树:

由上而下,由左及右的构造过程中,每一个节点都存在。
左子树height=右子树height 或左子树height=右子树height+1
(左满树)

2. 满二叉树:

k深树,且有2k - 1 个节点,且符合性质 第i层一定有 2k-1个节点

3. 线索二叉树:

规则:
L-son指向左儿子,如果没有左儿子,则指向线索前驱
R-son指向右儿子,如果没有右儿子,则指向线索后驱
(eg. 中序线索树:线索前驱即中序序列中的前一个点,线索后驱指中序序列中的后一个点)
作用: 提高遍历能力

4. 哈夫曼树

目的:记一点的 Va = 点权 * 点到根的距离,
为了让 Va 相对平均(求和较小),构造哈夫曼树
**操作思路:**每次都选取权值最小的两个点,进行连接。

priority_queue< <int,int> >q;
//q内存入所有点的<id,v>
int x1=q.top();q.pop();int x2=q.top();q.pop();
con(x1,x2,++cnt);//创建新节点cnt,并将x1和x2连接至该点
q.push( <cnt, x1+x2> );//放入队列	

性质1:树形状可能不唯一,但 Va 总和唯一
性质2:一定为完全二叉树
性质3:节点权值,从上而下,从大到小
哈夫曼编码:对于要访问的叶子节点,其哈夫曼编码=其访问路径。(访问路径用01字符串表示,0表示左儿子,1表示右儿子)
哈夫曼编码性质:无相同前缀

5. 中序、前序、后序、层序

定义:
在dfs遍历的过程中,处理节点和继续深搜两种操作的先后顺序。
记M、L、R分别表示对当前节点、左子树点集、右子树点集的操作顺序

中序:LMR

void dfs(int pos){
	dfs(left_child);
	work(pos);
	dfs(right_child);
}

后序:LRM

void dfs(int pos){
	dfs(left_child);
	dfs(right_child);
	work(pos);
}

前序:MLR

void dfs(int pos){
	work(pos);
	dfs(left_child);
	dfs(right_child);
}

层序:
BFS遍历,按层序顺序遍历。

6. 树、森林 transform into 二叉树

左孩子右兄弟 原则:

即孩子都在自己的左子树,兄弟都在自己的右子树 
另:对于森林而言,每个树的Root互相之间是兄弟关系。

三、常考考点

1. 树上节点数计算

满二叉树前提下

叶子节点数= 2 ^ (层数-1),总节点数=1+2+4+…+叶子节点树= 2^(层数) -1

完全二叉树前提下

n+1层完全二叉树,
总节点数= (2n - 1 ) (n层满二叉) + C (第n+1层的节点数)
叶子节点数 = C + 2n-1 - [C/2] (第n+1层节点是被第n层的 c/2个节点延申出来的)
(叶子节点数 = 第n+1层节点数 + 第n层的叶子节点数 )
易错点: 由叶子节点数反求总节点数最多的情况时时,C可以多取一个(成为奇数)。

度为n的树(※)

总结点数= (i:1~n)Σ i*n[i] + 1(根节点)
总结点数= (i:1~n)Σ n[i] + n[0](叶子节点数)

已知完全二叉树第n层的叶子节点个数为x

求该二叉树的总节点树 最多/最少
根据完全二叉树定义,该完全二叉树只有两种可能形状
第一种情况:
一共有n层,且第n层只有x个叶子节点,其余n-1层为满二叉树
第二种情况:
一共有n+1层,前n层构成满二叉树;且第n层有x个节点没有对应的n+1层的儿子,据此可计算第n+1层有多少个叶子节点;

2. 前中后层序⇒ 树形

所确定的树形的唯一性

前序+中序 或 中序+后序 或 中序+层序 可以唯一确定二叉树形
中序判断左右子树,前序、后序或层序判断谁是root
其余遍历序列组合 无法 唯一 确定

定形方法

LRroot = 后序
rootLR = 前序
L root R = 中序
该法用于判断灵活题目
(其中L和R表示左右子树的子序,即代表的是区间)

3. 普通树变二叉树

已知总节点数n,叶子节点数x,将其转化为二叉树后,二叉树中无右孩子的节点数 = n - x +1 (※)

证明:
根据 左孩子右兄弟原则
对于树上任意一个非叶子节点 pos,他的所有儿子们 有且仅有一个儿子会没有右孩子(最右边的兄弟)
所有非根节点 中,共有 n-x 个节点没有兄弟
根节点也没有兄弟
所以 ans = n - x + 1

(易错)任意树的后根遍历 = 对应二叉树的中序遍历

证明:根据左孩子右兄弟,画个图就懂了

4. 本章手写代码题考察:

求 子树大小 和 树深度

size[pos] = Σ size[to], size[pos] ++;
depth[pos] = max( depth[size[to] ) +1;
//代码略

※※※ 二叉树表示四则运算式 ※※※

中缀

即中序遍历,需要借助括号实现 [建议现场梳理一下思路]
下代码框内为思路,非代码

void dfs(pos){
	if(T[pos]->va){ cout<<va; return ;}//该点值为数字,则一定为叶子节点,直接输出
	work(lchild)//对于左儿子
		如果左儿子是数字,dfs(lchild) //不需要括号
		如果左儿子是比自己高优先级/同优先级的符号,dfs(lchild)//不需要括号
		如果左儿子是比自己低优先级的符号,cout<<"(", dfs(lchild),cout<<")";//需要括号
	cout<< T[pos]->sign;//输出当前符号
	work(rchild)//对于右儿子
		如果右儿子是数字,dfs(rchild)//不需要括号
		如果右儿子是比自己高优先级的符号,dfs(rchild)//不需要括号
		如果右儿子是比自己低优先级/同优先级的符号,cout<<"(", dfs(rchild),cout<<")";//需要括号
	return ;
}
后缀

即后序遍历,无需额外操作

前缀

即前序遍历,无需额外操作

※※※※ 非递归 写二叉树遍历 ※※※※

中序遍历
//遍历思路:先处理当前节点的左儿子;如果当前节点的左儿子部分已被处理完,则输出自己,并紧接着处理自己的右儿子(即让右儿子入栈)。
struct *stack[N];int top;
void midlle(struct *T){
	stack[++top]=T;struct *p=T;
	while(top){
		p=stack[top];top--;
		while(p!=NULL) stack[++top]=p->lchild, p=p->lchild;//左儿子入栈,直到叶节点
		if(top){
			p=stack[top];top-;//出栈
			print(p->id);//work该节点
			stack[++top]=p->rchild;//右儿子入栈
		}
	}
	return ;
}
前序遍历
struct *stack[N];int top;
void Pre(Struct *T){
	stack[++top]=T;
	while(top){
		struct *p=stack[top];top--;
		if(p==NULL) continue;
		print(p->id);
		stack[++top]=p->rchild; 
		stack[++top]=p->lchild;//先左儿子后右儿子
	}
	return ;
}
后序遍历
//左儿子(走到无左儿子为止)全进栈
//右儿子进栈(判重,避免重复进栈)
//若无左右儿子,当前节点出栈

5. 有关平衡树的考点 见 《查找》章节

查找–数据结构博客链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GoesM

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

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

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

打赏作者

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

抵扣说明:

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

余额充值