二叉树小记

小数据

  • 满二叉树的第 n n n ∗ ^{*} 层有 2 n − 1 2^{n-1} 2n1 个节点。
  • 高度为 n n n 的满二叉树的第 n n n 层节点总数为 2 n − 1 2^{n-1} 2n1 个。
  • 对于一个高度为 n n n k k k 叉树,其最多有 1 − k n 1 − k \frac{1-k^{n}}{1-k} 1k1kn 个节点。
    ∗ :根节点的编号为  1  。 \tiny{^{*}:根节点的编号为\ 1\ 。} :根节点的编号为 1 
    • preorder 前序遍历
    • inorder 中序遍历
    • postorder 后序遍历

用儿子表示法存储一棵二叉树及其遍历

儿子表示法,也就是开一个二维数组,将编号为 i i i 的节点的左、右儿子存储起来。

e.g. P4913 二叉树深度

题目描述

有一个 n ( n ≤ 1 0 6 ) n(n \le 10^6) n(n106) 个结点的二叉树。给出每个结点的两个子结点编号(均不超过 n n n),建立一棵二叉树(根节点的编号为 1 1 1),如果是叶子结点,则输入 0 0

建好这棵二叉树之后,请求出它的深度。二叉树的深度是指从根节点到叶子结点时,最多经过了几层。

输入格式

第一行一个整数 n n n,表示结点数。

之后 n n n 行,第 i i i 行两个整数 l l l r r r,分别表示结点 i i i 的左右子结点编号。若 l = 0 l=0 l=0 则表示无左子结点, r = 0 r=0 r=0 同理。

样例 #1
样例输入 #1
7
2 7
3 6
4 5
0 0
0 0
0 0
0 0

在输入格式中,可以发现,要输入第 i i i 个节点(第 i i i 行)的左、右儿子。用 i i i 作第一个下标,左儿子用 0 0 0 ,右儿子用 1 1 1 ,如:bt[i][0]代表第 i i i 个节点的左儿子,bt[i][1]代表第 i i i 个节点的右儿子。

for (int i=1;i<=n;i++)
	{
		int a,b;
		cin>>a>>b;
		bt[i][0]=a;//储存左儿子 
		bt[i][1]=b;//储存右儿子 
	}

遍历使用DFS,通过递归遍历左子树和右子树。

int dfs(int u)
{
	int l=0,r=0;
	if (bt[u][0]!=0) l=dfs(bt[u][0]);
	if (bt[u][1]!=0) r=dfs(bt[u][1]);
	return max(l,r)+1;
}

e.g. P1305 新二叉树

输入格式

第一行为二叉树的节点数 n n n。( 1 ≤ n ≤ 26 1 \leq n \leq 26 1n26)

后面 n n n 行,每一个字母为节点,后两个字母分别为其左右儿子。特别地,数据保证第一行读入的节点必为根节点。

空节点用 * 表示

样例 #1
样例输入 #1
6
abc
bdi
cj*
d**
i**
j**

本例比上一例要难一些(上一例给了编号,这一例给的是父亲节点)。在输入格式中,可以看到,要输入三个字符。第一个字符为父亲节点,第二、三个字符为左、右儿子节点。用ASCII码作第一个下标,存储节点标号(char)。

for (int i=1;i<=n;i++)
	{
		char a,b,c;
		cin>>a>>b>>c;
		if (i==1)
		{
			root=a;//记录根节点 ,与本方法无关
		}
		bt[a/*用char作下标,不需要将a~z转换为1~26*/][0]=b;//存储左儿子 
		bt[a][1]=c;//存储右儿子 
	}

通过递归输出用儿子表示法存储的二叉树的前、中、后序遍历

e.g. P1305 新二叉树

题目描述

输入一串二叉树,输出其前序遍历。

输出格式

二叉树的前序遍历。

样例 #1
样例输出 #1
abdicj

我们已经有了一棵用儿子表示法存储的二叉树了,那么怎样输出其前序遍历呢?

首先,我们知道,叶子结点为空,所以递归的结束条件为当前节点为叶子结点。然后,前序遍历的顺序是:根节点、左子树、右子树。所以,我们定义一个dfs函数,先输出当前节点,随后再遍历左子树和右子树。示例如下:

void dfs(char x)
{
	if (x=='*')//特判,不为叶子结点 
	{
		return ;
	}
	cout<<x;//输出根节点 
	if (bt[x][0]!='*')
	{
		dfs(bt[x][0]);//遍历左子树 
	}
	if (bt[x][1]!='*')
	{
		dfs(bt[x][1]);//遍历右子树 
	}
}

中序遍历就是把根节点移到中间。同理,后序就是把根节点移到最后。

用邻接表存储二叉树以及其遍历

e.g.P1364 医院设置

题目描述

设有一棵二叉树,如图:

其中,圈中的数字表示结点中居民的人口。圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为 1 1 1。如上图中,若医院建在 1 1 1 处,则距离和 = 4 + 12 + 2 × 20 + 2 × 40 = 136 =4+12+2\times20+2\times40=136 =4+12+2×20+2×40=136;若医院建在 3 3 3 处,则距离和 = 4 × 2 + 13 + 20 + 40 = 81 =4\times2+13+20+40=81 =4×2+13+20+40=81

输入格式

第一行一个整数 n n n,表示树的结点数。

接下来的 n n n 行每行描述了一个结点的状况,包含三个整数 w , u , v w, u, v w,u,v,其中 w w w 为居民人口数, u u u 为左链接(为 0 0 0 表示无链接), v v v 为右链接(为 0 0 0 表示无链接)。

样例 #1
样例输入 #1
5						
13 2 3
4 0 0
12 4 5
20 0 0
40 0 0

题目给出了每个节点的“邻居”,并没有说明是左儿子还是右儿子,也就是说,我们无法用儿子表示法存储。这个时候,我们就可以用邻接表进行存储。

邻接表的基本思想是,开设一个数组,随后储存该结点的所有链接。该过程使用std::vector实现。

	int n;
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		int w,u,v;
		cin>>w>>u>>v;
		f[i]=w;
		if (u!=0)
		{
			g[i].push_back(u);
			g[u].push_back(i);
		}
		if (v!=0)
		{
			g[i].push_back(v);
			g[v].push_back(i);
		}
	}

同儿子表示法一样,它也是用DFS进行遍历。

int dfs(int u,int d,int fa)
{
	int sum=f[u]*d;
	for (auto v:g[u])//遍历当前节点的所有链接
	{
		if (v==fa)//判断是否走到了父节点。如果走到了父节点,那么就会进入死循环,必须特判
		{
			continue;
		}
		sum+=dfs(v,d+1,u);//下一个节点的代价
	}
	return sum;
}
  • 45
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值