二叉树与树

二叉树的概念与遍历

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;
}

二叉树遍历的意思是将一棵二叉树从根结点开始,按照指定顺序,不重复、不遗漏地访问每一个结点。在完成一些任务中,必须要访问所有结点的信息,那么就需要按照某种方式不重复、不遗漏地访问所有结点。

image

image

答案

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\) 个,而是一个任意正整数,这棵树的形态是?

image

image

答案

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;
}
posted @ 2024-08-13 08:50  RonChen  阅读(99)  评论(0编辑  收藏  举报