通过前几天二叉树和并查集基础知识的学习,对于这一块有了一定的理解和解题的思路,所以这两天主要就是在完成洛谷团队上的题目(二叉树为主)
P1827 [USACO3.4] 美国血统 American Heritage - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形的方法。
你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的 后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两 种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多于 2626 个的顶点。
这是在样例输入和样例输出中的树的图形表达方式:
C
/ \
/ \
B G
/ \ /
A D H
/ \
E F
附注:
第一行一个字符串,表示该树的中序遍历。
第二行一个字符串,表示该树的前序遍历。
单独的一行表示该树的后序遍历。
输入
ABEDFCHG CBADEFGH
输出
AEFDBHGC
思路:这里我们要根据中序遍历和前序遍历来求后序遍历,由前序遍历(根左右)可知root(根节点)的位置,要求后序遍历我们先要找到root在中序遍历中的位置(pos表示),我们可以知道一个特性就是在这两个不同的遍历中左子树和右子树的长度是不变的(位置会发生变化),我们分别把两种遍历的左右子树表示出来,分别对左右子树进行递归搜索,每次搜索完之后将当前root排除(打印)
代码:
#include
#include
#include
using namespace std;
void dfs(string s1, string s2) {
if (s1.length() == 0)
return;
int len = s1.length();
char root = s2[0];
int pos = s1.find(root);
string s1l = s1.substr(0, pos), s1r = s1.substr(pos + 1, len - 1 - pos);
string s2l = s2.substr(1, pos), s2r = s2.substr(pos+1, len - 1-pos);
dfs(s1l, s2l);
dfs(s1r, s2r);
cout << root;
}
int main() {
string s1, s2;
cin >> s1 >> s2;
dfs(s1, s2);
return 0;
}
P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数 ≤8≤8)。
共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
共一行一个字符串,表示一棵二叉树的先序。
输入
BADC BDCA
输出
ABCD
思路:这道题与上面的美国血统相似度非常高,只不过这里是根据后序和中序来求先序(我们在每次搜索前排出当前root即可)
代码:
#include
#include
using namespace std;
void dfs(string s1,string s2) {
if (s1.length() == 0)return;
int len = s1.length();
char root = s2[len - 1];
cout << root;
int pos = s1.find(root);
string s1l = s1.substr(0, pos), s1r = s1.substr(pos + 1, len - 1 - pos);
string s2l = s2.substr(0, pos), s2r = s2.substr(pos , len - 1-pos);
dfs(s1l, s2l);
dfs(s1r, s2r);
}
int main() {
string s1, s2;
cin >> s1 >> s2;
dfs(s1, s2);
return 0;
}
P1305 新二叉树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
输入一串二叉树,输出其前序遍历。
第一行为二叉树的节点数 n。(1≤n≤26)
后面 n 行,每一个字母为节点,后两个字母分别为其左右儿子。特别地,数据保证第一行读入的节点必为根节点。
空节点用 *
表示
二叉树的前序遍历。
输入
6 abc bdi cj* d** i** j**
输出
abdicj
思路:先用结构体构建一个二叉树,依次将结点输入,从根节点开始进行搜索,因为‘*’为空,左子树不为‘*’时就继续对左子树进行搜索,右子树同样如此,每次搜索前都将当前结点进行输出(求的是前序遍历)
代码:
#include
using namespace std;
struct node {
char l, r;
}tree[151];
void dfs(char bg) {
cout << bg;
if (tree[bg].l != '*')dfs(tree[bg].l);
if (tree[bg].r != '*')dfs(tree[bg].r);
}
int main() {
int n;
cin >> n;
char a,begin, l, r;
cin >> begin >> l >> r;
tree[begin].l = l;
tree[begin].r = r;
for (int i = 1; i < n; i++) {
cin >> a >> l >> r;
tree[a].l = l;
tree[a].r = r;
}
dfs(begin);
return 0;
}
P4913 【深基16.例3】二叉树深度 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
有一个 n(≤10^6)n(n≤106) 个结点的二叉树。给出每个结点的两个子结点编号(均不超过 n),建立一棵二叉树(根节点的编号为 11),如果是叶子结点,则输入 0 0
。
建好这棵二叉树之后,请求出它的深度。二叉树的深度是指从根节点到叶子结点时,最多经过了几层。
第一行一个整数 n,表示结点数。
之后 n 行,第 i 行两个整数 l、r,分别表示结点 i 的左右子结点编号。若l=0 则表示无左子结点,r=0 同理。
一个整数,表示最大结点深度。
输入
7 2 7 3 6 4 5 0 0 0 0 0 0 0 0
输出
4
思路:结构体构建二叉树,依次输入完结点后直接从根结点开始搜索,当左右结点均为0时说明二叉树搜索完毕(把这个时候的深度返回),没有走到0时分别对左右子树进行搜索(每次搜索深度+1).
代码:
#include
using namespace std;
struct node {
int l, r;
}tree[1000001];
int ans = -1;
void dfs(int pos,int deep) {
if (tree[pos].l == 0 && tree[pos].r == 0) {
ans = max(ans, deep);
return;
}
dfs(tree[pos].l, deep+1);
dfs(tree[pos].r, deep + 1);
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> tree[i].l >> tree[i].r;
dfs(1,1);
cout << ans << endl;
return 0;
}
P1229 遍历问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:
所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。
共两行,第一行表示该二叉树的前序遍历结果 s1,第二行表示该二叉树的后序遍历结果 s2。
输出可能的中序遍历序列的总数,结果不超过 2^63−1。
输入
abc cba
输出
4
思路: (看题解看到的规律)在一棵树上若有一个结点是只有一个子结点的那么这个子结点在左在右不影响先序后序的遍历顺序,那么总树数就要乘以2(即前序遍历的根结点和它后面遍历的第一个结点与后序遍历的根结点和它的前一个结点一一相等对应)
代码:
#include
using namespace std;
int ans = 1;
int main() {
string s1,s2;
cin >> s1 >> s2;
for (int i = 0; i <= s1.length()-2; i++) {
for (int j = 0; j <= s2.length()-1; j++) {
if (s1[i] == s2[j] && s1[i + 1] == s2[j - 1])ans *= 2;
}
}
cout << ans;
return 0;
}