寒假集训——树

2024.1.26 ,今天的内容比较难,树和树形dp,不过树我已经够呛了,树dp还是等等学吧。今天主要学了如何建树,树的前序、中序和后序遍历、字典树。

先介绍一下树//

名词介绍

1.父节点/子节点:一个节点的上个节点是它的父节点,下面连接的其他节点是子节点。

2.根节点:指最最最祖宗的那个节点,根节点没有父亲节点,树中的每个点都可以成为根节点。

3.叶子节点:指最最最儿子的那个节点,叶子节点没有子节点。

4.子树:某个节点的子树是指把上面父节点连接的树剪去,剩下的就是它的子树。比如图一如果以5为根节点,那么节点3的子树就是3,1,2。

树的特殊性质

1.图中有n个点,那么边数只能为 (n-1) 条边。 因为(除了根节点)每个点有且仅有一个父亲节点,那么每个点就会有一条边连接到父亲节点,一共n个点,减去根节点,那么就只剩下 (n-1) 条边了。

2.树的dfs的时间复杂度是O(n) 的。因为每个点只会被遍历一次。

存图方式

首选推荐vector数组建树(重要),其次链式前向星也可以,链表指针建树的方法由于编码太麻烦,竞赛基本不用。但课程上程设II和数据结构可能会用到,这里偏向竞赛,所以不讲。

链式前向星好像比较难,我还没有学,今天先学的vector数组建树,感觉还行。

接下来是讲完后我练习的题目。

P4913 【深基16.例3】二叉树深度

结构体建树,求二叉树的深度

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e6 + 10;
const int M = 10100;
const int mod = 1e9 + 7;
int minn = 1e18;
int maxx = -1e18;
int cnt, sum;
int n, m;
struct node {
	int l, r;
} a[N];
signed dfs(int x) {
	if (x == 0)
		return 0;
	int l, r;
	l = dfs(a[x].l);
	r = dfs(a[x].r);
	int h = max(l, r) + 1;
	return h;
}
signed main() {
	cin >> n;
	int i;
	for (i = 1; i <= n; i++) {
		cin >> a[i].l >> a[i].r;
	}
	cout << dfs(1) << endl;
}

B3861 子树和

这个题主演考察的是树的建立,在建立树的同时记录结点的数量。

#include<bits/stdc++.h> 
using namespace std;

#define int long long 
#define endl '\n'

const int N = 2e6 + 100;
const int M = 10100;

int cnt, sum;

vector<int> t[M]; // 定义一个数组,每个元素都是一个整数向量。这实际上是一个邻接表,表示图的结构

int ans[10000]; // 定义一个数组,用于存储每个节点遍历的结果

void dfs(int x, int f) // 定义一个深度优先搜索函数
{
	int i;
	for (i = 0; i < t[x].size(); i++) // 遍历x的所有邻接节点
	{
		int ne = t[x][i]; // 获取x的邻接节点ne
		if (ne == f) // 如果ne是父节点f,则跳过
			continue;
		dfs(ne, x); // 对ne进行深度优先搜索,以x为父节点
		ans[x] += ans[ne]; // 将ne的遍历结果加到x上
	}
	ans[x] += 1; // 将1加到x的遍历结果上,表示x本身被访问了
}

signed main() // main函数,程序的入口点。注意这里应该是signed main(),而不是signed main()。这里可能是个错误。
{
	int n; // 定义一个整数n,表示图中节点的数量
	cin >> n; // 从输入读取n的值
	int i;
	for (i = 2; i <= n; i++) // 从节点2开始,遍历所有节点到n
	{
		int x; // 定义一个整数x,表示当前要添加的邻接节点
		cin >> x; // 从输入读取x的值
		t[i].push_back(x); // 将x添加到节点i的邻接表中
		t[x].push_back(i); // 将i添加到节点x的邻接表中
	}
	dfs(1, 0); // 从节点1开始进行深度优先搜索,以0为父节点。这里假设1是源节点或起始节点。
	for (i = 1; i <= n; i++) // 遍历所有节点,从1开始到n
	{
		cout << ans[i] << endl; // 输出节点i的遍历结果,并换行。这里假设ans[i]表示从源节点到节点i的路径长度。
	}
}

接下来是重要的树的建立策略方案(八股)

数据结构实验之二叉树二:遍历二叉树

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6 + 100;
const int M = 10100;
int cnt, sum;
string s;
int idx, now;
struct node
{
	char data;
	int l, r;
} a[N];

int build()//建立函数
{
	a[++now].data = s[idx++];//开始建立
	if (a[now].data == ',')//,说明没有子节点了
	{
		return 0;
	}
	int x = now;
	a[x].l = build();//递推左儿子
	a[x].r = build();//递推右儿子
	return x;//返回根节点
}
void mid(int x)
{
	if (x == 0)
		return;
	mid(a[x].l);
	cout << a[x].data;
	mid(a[x].r);

}

void last(int x)
{
	if (x == 0)
		return;
	last(a[x].l);
	last(a[x].r);
	cout << a[x].data;
}
signed main()
{
	while (cin >> s)
	{
		idx = 0, now = 0;
		int root = build();
		mid(root);//中
		cout << endl;
		last(root);//后
		cout << endl;
	}
}

数据结构实验之二叉树三:统计叶子数

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6 + 100;
const int M = 10100;
int cnt, sum;
string s;
int idx, now;
struct node
{
	char data;
	int l, r;
} a[N];

int build()
{
	a[++now].data = s[idx++];
	if (a[now].data == ',')
	{
		return 0;
	}
	int x = now;
	a[x].l = build();
	a[x].r = build();
	return x;//返回节点
}

void dfs(int x)//深搜
{
	if (x == 0)
		return;
	dfs(a[x].l);
	dfs(a[x].r);//不断递归
	if (a[x].l == 0 && a[x].r == 0)//0,0说明搜到叶子了
	{
		cnt++;
	}
}
signed main()
{
	while (cin >> s)
	{
		idx = 0, now = 0;
		cnt = 0;
		int root = build();
		dfs(root);//开搜
		cout << cnt << endl;
	}
//淹没心底的景观
}

数据结构实验之二叉树五:层序遍历

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6 + 100;
const int M = 10100;
int cnt, sum;
string s;
int idx, now;
queue<int>st;
struct node
{
	char data;
	int l, r;
} a[N];
int build()
{
	a[++now].data = s[idx++];
  //建表方法
	if (a[now].data == ',')
		return 0;
	int x = now;
	a[x].l = build();
	a[x].r = build();
	return x;
}
void bfs(int x)//宽搜
{
	if (x)
		st.push(x);
	while (!st.empty())
	{
		auto t = st.front();
		if (a[t].l != 0)
			st.push(a[t].l);
		if (a[t].r != 0)
			st.push(a[t].r);
		cout << a[t].data;
		st.pop();
	}
}
signed main()
{
	int n, i;
	cin >> n;
	for (i = 1; i <= n; i++)
	{
		idx = 0, now = 0;
		cin >> s;
		int root = build();
		bfs(root);
		cout << endl;
	}
}

B3642 二叉树的遍历

这个题就是经典的树的遍历的模板题了。

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define endl '\n'

const int N = 2e6 + 100;
const int M = 10100;

struct node // 定义一个结构体,用于表示树节点
{
	int l, r; // 节点的左子节点和右子节点
	int id; // 节点的ID
} a[N]; // 定义一个数组,每个元素都是一个node结构体。这实际上是一个二叉树。

void pre(int x) // 前序遍历函数
{
	if (x == 0) // 如果当前节点为空,直接返回
		return;
	cout << a[x].id << " "; // 输出当前节点的ID,并在其后添加一个空格
	pre(a[x].l); // 递归遍历左子节点
	pre(a[x].r); // 递归遍历右子节点
}

void mid(int x) // 中序遍历函数
{
	if (x == 0) // 如果当前节点为空,直接返回
		return;
	mid(a[x].l); // 递归遍历左子节点
	cout << a[x].id << " "; // 输出当前节点的ID,并在其后添加一个空格
	mid(a[x].r); // 递归遍历右子节点
}

void last(int x) // 后序遍历函数
{
	if (x == 0) // 如果当前节点为空,直接返回
		return;
	last(a[x].l); // 递归遍历左子节点
	last(a[x].r); // 递归遍历右子节点
	cout << a[x].id << " "; // 输出当前节点的ID,并在其后添加一个空格
}

signed main() // 主函数,程序的入口点。注意这里应该是signed main(),而不是signed main()。这里可能是个错误。
{
	int n; // 定义一个整数n,表示图中节点的数量
	cin >> n; // 从输入读取n的值
	int i;
	for (i = 1; i <= n; i++) // 从节点1开始,遍历所有节点到n
	{
		int l, r; // 定义两个整数l和r,表示当前节点的左右子节点ID
		cin >> l >> r; // 从输入读取l和r的值
		a[i].l = l; // 将l赋值给当前节点的左子节点ID
		a[i].r = r; // 将r赋值给当前节点的右子节点ID
		a[i].id = i; // 将当前节点的ID设置为i
	}
	pre(1);
	cout << endl;
	mid(1);
	cout << endl;
	last(1);
	cout << endl;
}

P1305 新二叉树

这个题主演是考察的前序遍历,有个小技巧就是它不像之前的题给出你节点的数,而

是给出了字母,但是这也没有影响,我们只要把这个字母直接记录到数组的下标就可

以了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6 + 100;
const int M = 10100;
char idx, now;
struct node
{
	char l, r;
	char data;
} a[N];
void pre(char x)
{
	if (x == '*')
		return;
	cout << x ;
	pre(a[x].l);
	pre(a[x].r);
}
signed main()
{
	int n;
	cin >> n;
	int i;
	cin >> idx;
	cin >> a[idx].l >> a[idx].r;
	for (i = 2; i <= n; i++)
	{
		cin >> now;
		cin >> a[now].l >> a[now].r;
	}
	pre(idx);
	return 0;
}

P8625 [蓝桥杯 2015 省 B] 生命之树

树形dp,不会,摆着吧。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e6 + 100;
const int M = 10100;
int cnt, sum;
int maxx = 0;
vector<int>a[N];
int dp[N];
void dfs(int x, int f)
{
	int i;
	for (i = 0; i < a[x].size(); i++)
	{
		int ne = a[x][i];
		if (ne == f)
			continue;
		dfs(ne, x);
		dp[x] = max(dp[x], dp[x] + dp[ne]);
	}
}
signed main()
{
	int n;
	cin >> n;
	int i;
	for (i = 1; i <= n; i++)
		cin >> dp[i];
	for (i = 1; i < n; i++)
	{
		int x, y;
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	dfs(1, 0);
	for (i = 1; i <= n; i++)
	{
		maxx = max(maxx, dp[i]);
	}
	cout << maxx << endl;
}
posted @ 2024-07-05 17:40  ZhangDT  阅读(8)  评论(0编辑  收藏  举报