二叉树与树
二叉树的概念与遍历
P4913
#include <cstdio>
#include <algorithm>
using std::max;
const int N = 1e6 + 5;
int l[N],r[N];
int height(int u) { // 计算以u为根节点的子树的高度
if (u==0) return 0;
return 1+max(height(l[u]),height(r[u]));
}
int main()
{
int n; scanf("%d",&n);
for (int i=1;i<=n;i++) {
int x,y; scanf("%d%d",&x,&y);
l[i]=x;r[i]=y;
}
printf("%d\n", height(1));
return 0;
}
二叉树遍历的意思是将一棵二叉树从根结点开始,按照指定顺序,不重复、不遗漏地访问每一个结点。在完成一些任务中,必须要访问所有结点的信息,那么就需要按照某种方式不重复、不遗漏地访问所有结点。
答案
D
P1305
#include <cstdio>
const int N = 30;
char s[10];
int l[N],r[N];
void dfs(int u) {
if (u==0) return;
// 先序遍历:根左右
char ch=u-1+'a';
printf("%c", ch);
dfs(l[u]); dfs(r[u]);
}
int main()
{
int n; scanf("%d",&n);
int root;
for (int i=1;i<=n;i++) {
scanf("%s", s);
// s[0] s[1] s[2]
int u=s[0]-'a'+1;
int left=(s[1]=='*' ? 0 : s[1]-'a'+1);
int right=(s[2]=='*' ? 0 : s[2]-'a'+1);
l[u]=left; r[u]=right;
if (i==1) root=u;
}
dfs(root);
return 0;
}
P1827
#include <cstdio>
#include <cstring>
const int N = 30;
char mid[N], pre[N]; // 中、前
void solve(int l1,int r1,int l2,int r2) {
if (l1>r1 || l2>r2) return;
// printf("mid[%d,%d], pre[%d,%d]\n", l1,r1,l2,r2);
// mid[l1...r1] pre[l2...r2]
char root=pre[l2];
// 在中序遍历中查找根节点的位置
int pos;
for (int i=l1;i<=r1;i++) {
if (mid[i]==root) {
pos=i; break;
}
}
// pos就是中序的根节点位置
// 左子树 mid[l1...pos-1] 右子树 mid[pos+1...r1]
// 左子树大小 pos-l1
// 右子树大小 r1-pos
// pre[l2+1...l2+(pos-l1)]
// pre[r2-(r1-pos)+1...r2]
solve(l1,pos-1,l2+1,l2+(pos-l1));
solve(pos+1,r1,r2-(r1-pos)+1,r2);
printf("%c",root);
}
int main()
{
scanf("%s%s",mid+1,pre+1);
int len=strlen(mid+1);
solve(1,len,1,len);
return 0;
}
P1229
#include <iostream>
#include <string>
using std::string;
using std::cin;
using std::cout;
using ll = long long;
int main()
{
string s1,s2;
cin>>s1>>s2;
ll ans=1;
// 找会有多少个前序AB后序BA的
int len=s1.size();
for (int i=0;i<=len-2;i++) {
// s1[i] s1[i+1]
for (int j=0;j<=len-2;j++) {
// 是否存在 s2[j]-s1[i+1] s2[j+1]-s1[i]
if (s2[j]==s1[i+1] && s2[j+1]==s1[i]) {
ans*=2; break;
}
}
}
cout<<ans<<"\n";
return 0;
}
例题:P4715 【深基16.例1】淘汰赛
#include <cstdio>
#include <algorithm>
using std::max;
using std::min;
const int N = 300;
int tree[N],winner[N], len;
void dfs(int u) {
if (u>=len) return;
dfs(u*2); dfs(u*2+1);
if (tree[u*2]>tree[u*2+1]) {
tree[u]=tree[u*2];
winner[u]=winner[u*2];
} else {
tree[u]=tree[u*2+1];
winner[u]=winner[u*2+1];
}
}
int main()
{
int n; scanf("%d",&n);
len=1;
for (int i=1;i<=n;i++) len*=2;
for (int i=len;i<=2*len-1;i++) {
scanf("%d",&tree[i]);
winner[i]=i-len+1;
}
dfs(1);
printf("%d\n", tree[2]<tree[3]?winner[2]:winner[3]);
return 0;
}
如果国家个数不是 \(2^n\) 个,而是一个任意正整数,这棵树的形态是?
答案
B。前 \(4\) 层都是满的,第 \(5\) 层至少有一个结点,因此至少有 \(1+2+4+8+1=16\) 个结点。
二叉树的综合应用
P1364
#include <cstdio>
#include <algorithm>
using std::min;
const int N = 105;
int n,ans,people;
int w[N],l[N],r[N],depth[N],s[N];
// depth记录每个节点的深度,s记录每个节点的子树人口总量
bool flag[N];
void dfs(int u, int d) {
if (u==0) return;
depth[u]=d;
dfs(l[u],d+1); dfs(r[u],d+1);
s[u]=w[u]+s[l[u]]+s[r[u]];
}
void solve(int u, int sum) { // 医院换到u时,距离和为sum
if (u==0) return;
// printf("sum=%d\n",sum);
ans=min(ans,sum);
// u->l[u]
// 此时距离和的变化
// l[u]的子树(s[l[u]])距离全都缩小了1
// 其他人(总人口数-s[l[u]])距离增大1
// 距离和 - s[l[u]] + (总人口数-s[l[u]])
solve(l[u],sum-2*s[l[u]]+people);
solve(r[u],sum-2*s[r[u]]+people);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) {
flag[i]=true; // 先假设它是根节点
}
for (int i=1;i<=n;i++) {
scanf("%d%d%d",&w[i],&l[i],&r[i]);
people+=w[i];
flag[l[i]]=false;
flag[r[i]]=false; // 被作为孩子就不可能是根节点
}
int root;
for (int i=1;i<=n;i++) {
if (flag[i]) {
root=i; break;
}
}
// printf("root=%d\n",root);
dfs(root,0);
ans=0;
// 先计算假如以root作为医院位置,距离和是多少
for (int i=1;i<=n;i++) ans+=w[i]*depth[i];
// 接下来要考虑换其他位置作为医院,距离和的变化
solve(root,ans);
printf("%d\n",ans);
return 0;
}
从二叉树到多叉树
P5908
#include <cstdio>
#include <vector>
using std::vector;
const int N = 1e5+5;
vector<int> tree[N]; // tree[i]存储与i相邻的点
int d, ans;
void dfs(int u, int depth, int from) {
ans++;
if (depth==d) return;
// 当前节点是u,深度是depth,来源是from
int len=tree[u].size();
for (int i=0;i<len;i++) {
int v=tree[u][i]; // u->v
if (v!=from) {
dfs(v,depth+1,u);
}
}
}
int main()
{
int n; scanf("%d%d",&n,&d);
for (int i=1;i<=n-1;i++) {
int u,v;
scanf("%d%d",&u,&v);
// 建树时取双向边
tree[u].push_back(v);
tree[v].push_back(u);
}
dfs(1,0,0);
printf("%d\n",ans-1);
return 0;
}
P2052
#include <cstdio>
#include <vector>
#include <cmath>
using std::vector;
using std::abs;
using ll = long long;
const int N = 1e6+5;
struct Edge {
int to,l;
};
vector<Edge> tree[N]; // tree[i]存储i连出去的边
int s[N], n; // s[i]记录以i为根的子树大小(在以1为整棵树的根的大背景下)
// s[i]=1(自己)+s[v1]+s[v2]+...(每一棵子树)
ll ans; // 总费用
/*
void dfs(int u, int from) {
// 当前节点是u,来源是from
int len=tree[u].size();
s[u]=1;
for (int i=0;i<len;i++) {
Edge e=tree[u][i];
if (e.to!=from) {
// u->e.to
dfs(e.to,u);
// 回溯 此时 s[e.to]已成定值
// 针对 u----e.to 这条边计算它的费用
// 两边国家数 s[e.to] n-s[e.to]
// e.l * abs(n-2*s[e.to])
ans+=1ll*e.l*abs(n-2*s[e.to]);
s[u]+=s[e.to]; // 加上下面每棵子树的大小
}
}
}
*/
void dfs(int u, int from) {
// 当前节点是u,来源是from
s[u]=1;
// range-for
for (Edge e : tree[u]) {
if (e.to!=from) {
// u->e.to
dfs(e.to,u);
// 回溯 此时 s[e.to]已成定值
// 针对 u----e.to 这条边计算它的费用
// 两边国家数 s[e.to] n-s[e.to]
// e.l * abs(n-2*s[e.to])
ans+=1ll*e.l*abs(n-2*s[e.to]);
s[u]+=s[e.to]; // 加上下面每棵子树的大小
}
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n-1;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
// 建树时取双向边
tree[u].push_back({v,w});
tree[v].push_back({u,w});
}
// 整棵树以谁为根无所谓(无根树)
// 假设强制以1为根
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
P2420
#include <cstdio>
#include <vector>
#include <cmath>
using std::vector;
using std::abs;
using ll = long long;
const int N = 1e5+5;
struct Edge {
int v,w;
};
vector<Edge> tree[N]; // tree[i]存储i连出去的边
int n,s[N]; // s[i] 从根节点(假设为1)到i的一路上的边权异或起来
void dfs(int u, int from) {
// 当前节点是u,来源是from
// range-for
for (Edge e : tree[u]) {
if (e.v!=from) {
// u->e.v
// 1到v的异或等于1到u的异或再异或上这条边
s[e.v]=s[u]^e.w;
dfs(e.v,u);
}
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n-1;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
// 建树时取双向边
tree[u].push_back({v,w});
tree[v].push_back({u,w});
}
// 整棵树以谁为根无所谓(无根树)
// 假设强制以1为根
dfs(1,0);
int m; scanf("%d",&m);
for (int i=1;i<=m;i++) {
int u,v; scanf("%d%d",&u,&v);
printf("%d\n",s[u]^s[v]);
}
return 0;
}