寒假集训——树
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;
}