题目背景 关于 dfs 序是访问一棵树时将其按照 dfs 时访问的先后顺序打上序号。于是,通过 O(n) 的扫一遍树上的每个点就可以把非线性的树形结构转成线性的区间结构。 题目描述 给定一颗树,输出这棵树的dfs序,并且输出dfs的入时间戳和出时间戳。根节点默认为1的无向图。 输入格式 输入节点的个数 n (n<=10^5) 接下来输入 n-1 行,每次输入两个节点代表一条边。 边集数组来构成这颗树。 输出格式 输出这颗树的dfs序,同一层的节点,优先考虑数字小的节点。 然后换行输出dfs序的入时间戳序列。 换行输出dfs序的出时间戳序列。 输入输出样例 输入 #1复制 9 1 2 1 3 2 4 2 5 3 6 5 7 5 8 7 9 输出 #1复制 124579836 128349576 979379676 DP小鲸鱼,帮我用链式前向星和vector两种做法为这道题各写一篇C++代码。
时间: 2025-07-16 16:03:12 浏览: 3
<think>嗯,用户想要用链式前向星和vector分别实现树的DFS序生成,并且记录入栈和出栈时间。首先我得回忆一下这两种数据结构在存储树结构时的不同实现方式。
链式前向星的话,通常是用数组来模拟链表,每个节点都有一个头指针,指向该节点的第一条边,然后每条边保存下一个边的索引。DFS遍历的时候,需要从头指针开始,沿着next指针遍历所有邻接节点。不过要注意,无向图的话,每条边会被存储两次,所以遍历的时候需要避免回溯到父节点,否则会死循环。所以在DFS函数里,要记录当前节点的父节点,然后遍历邻接节点时跳过父节点。
然后是vector实现,这个相对简单,每个节点对应一个vector,里面存储所有连接的节点。同样,在DFS的时候需要防止回溯,所以每次访问子节点时,要检查是否已经访问过,或者是否是父节点。
接下来是DFS序的问题。DFS序一般包括入栈时间(进入节点的时间戳)和出栈时间(离开节点的时间戳)。通常,入栈时间是在第一次访问该节点时记录,出栈时间是在回溯时,所有子节点处理完毕后记录。这两个时间戳可以用来判断节点之间的祖先关系等。
对于代码结构,链式前向星需要先构建图的存储结构,包括边数组和head数组。然后进行DFS,每次访问节点时记录入栈时间,递归访问所有子节点(排除父节点),最后在返回前记录出栈时间。同样,vector的实现也是类似,只不过邻接表的访问方式不同。
需要注意的是,时间戳的递增顺序。每次进入节点时增加时间戳,离开时再增加。例如,入栈时间是进入时记录,然后处理子节点,处理完所有子节点后,离开时记录出栈时间。
然后要处理输入输出,根据不同的存储结构,初始化方式也不同。链式前向星需要逐个添加边,而vector则是push_back到对应的位置。
可能遇到的问题包括:时间戳的正确递增,避免重复访问父节点,以及如何处理大规模数据时的效率问题。例如,链式前向星在遍历时需要用循环遍历每个邻接边,而vector则可以直接用范围for循环。
另外,用户提供的引用中有提到链式前向星的遍历代码,以及vector存储树结构的方式,这些可以作为参考。例如,引用3中提到了用vector数组存储树,每个节点的连接节点都存在对应的vector里。而引用2中链式前向星的遍历方式,使用head数组和循环。
最后,要确保代码示例正确,包括变量的初始化,DFS的递归实现,以及时间戳的处理。同时,测试用例的输入输出是否符合预期,比如树的重心问题中的输入样例,可以用来验证代码的正确性。</think>以下分别给出链式前向星和vector实现DFS序的C++代码,包含入栈时间和出栈时间标记:
### 一、链式前向星实现
```cpp
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int head[N], cnt = 0;
int in[N], out[N], timestamp = 0;
struct Edge {
int to;
int next;
} edges[N*2];
void addEdge(int u, int v) {
edges[++cnt].to = v;
edges[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int u, int father) {
in[u] = ++timestamp; // 记录入栈时间
for (int i = head[u]; i; i = edges[i].next) {
int v = edges[i].to;
if (v != father) { // 防止回溯
dfs(v, u);
}
}
out[u] = ++timestamp; // 记录出栈时间
}
int main() {
memset(head, 0, sizeof(head));
int n;
cin >> n;
// 建图
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
addEdge(a, b);
addEdge(b, a);
}
dfs(1, -1); // 假设根节点为1
// 输出DFS序
for (int i = 1; i <= n; i++) {
cout << "Node " << i << ": in=" << in[i] << ", out=" << out[i] << endl;
}
return 0;
}
```
### 二、vector实现
```cpp
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
vector<int> edges[N];
int in[N], out[N], timestamp = 0;
void dfs(int u, int father) {
in[u] = ++timestamp; // 记录入栈时间
for (int v : edges[u]) {
if (v != father) { // 防止回溯
dfs(v, u);
}
}
out[u] = ++timestamp; // 记录出栈时间
}
int main() {
int n;
cin >> n;
// 建图
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
edges[b].push_back(a);
}
dfs(1, -1); // 假设根节点为1
// 输出DFS序
for (int i = 1; i <= n; i++) {
cout << "Node " << i << ": in=" << in[i] << ", out=" << out[i] << endl;
}
return 0;
}
```
### 代码要点说明:
1. **时间戳生成**:每个节点$u$的$in[u]$在首次访问时记录,$out[u]$在所有子节点访问完成后记录
2. **防回溯机制**:通过记录父节点参数避免重复访问(关键差异点)
3. **存储差异**:
- 链式前向星通过`head[]`数组索引边集合
- vector直接存储邻接表
4. **时间复杂度**:两者均为$O(n)$,但vector实现常数更小[^3]
输入样例测试(引用[4]的案例):
```
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
```
输出结果将展示每个节点的访问顺序和回溯时间,可用于后续子树规模计算等操作。
阅读全文
相关推荐












