小数据
- 满二叉树的第 n n n ∗ ^{*} ∗ 层有 2 n − 1 2^{n-1} 2n−1 个节点。
- 高度为 n n n 的满二叉树的第 n n n 层节点总数为 2 n − 1 2^{n-1} 2n−1 个。
- 对于一个高度为
n
n
n 的
k
k
k 叉树,其最多有
1
−
k
n
1
−
k
\frac{1-k^{n}}{1-k}
1−k1−kn 个节点。
∗ :根节点的编号为 1 。 \tiny{^{*}:根节点的编号为\ 1\ 。} ∗:根节点的编号为 1 。- preorder 前序遍历
- inorder 中序遍历
- postorder 后序遍历
用儿子表示法存储一棵二叉树及其遍历
儿子表示法,也就是开一个二维数组,将编号为 i i i 的节点的左、右儿子存储起来。
e.g. P4913 二叉树深度
题目描述
有一个
n
(
n
≤
1
0
6
)
n(n \le 10^6)
n(n≤106) 个结点的二叉树。给出每个结点的两个子结点编号(均不超过
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 1≤n≤26)
后面 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;
}