Medium
题目描述
有一个包含 n 个节点的无向树,节点编号从 0 到 n - 1,共有 n - 1 条边。
给你一个长度为 n - 1 的二维整数数组 edges,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。同时给你一个整数数组 restricted 表示受限制的节点。
返回在不访问受限制节点的情况下,从节点 0 出发能够到达的节点数目的最大值。
注意节点 0 不会是受限制的节点。
示例 1:
输入:n = 7, edges = [[0,1],[1,2],[3,1],[4,0],[0,5],[5,6]], restricted = [4,5]
输出:4
解释:上图显示了这棵树。
我们有 [0,1,2,3] 是从节点 0 出发在不访问受限制节点的情况下唯一可以到达的节点。
示例 2:
输入:n = 7, edges = [[0,1],[0,2],[0,5],[0,4],[3,2],[6,5]], restricted = [4,2,1]
输出:3
解释:上图显示了这棵树。
我们有 [0,5,6] 是从节点 0 出发在不访问受限制节点的情况下唯一可以到达的节点。
约束条件:
2 <= n <= 10^5edges.length == n - 1edges[i].length == 20 <= ai, bi < nai != biedges表示一个有效的树1 <= restricted.length < n1 <= restricted[i] < nrestricted的所有值都是唯一的
解题思路
这道题要求从节点 0 出发,在不经过受限制节点的情况下,统计能够访问的节点数量。
解题思路:
图的构建:首先需要根据
edges构建邻接表表示的无向图,方便后续遍历。标记受限节点:将
restricted数组中的节点标记为不可访问,可以使用哈希集合快速查询。图遍历:从节点 0 开始进行 DFS 或 BFS 遍历,在遍历过程中:
- 跳过已访问的节点
- 跳过受限制的节点
- 对每个可访问的节点进行计数
计数统计:遍历过程中统计访问到的节点数量即为答案。
算法选择:
- DFS(深度优先搜索):递归实现简洁,适合树结构
- BFS(广度优先搜索):迭代实现,内存使用更可控
两种方法的时间复杂度都是 O(n),空间复杂度也都是 O(n)。这里推荐使用 DFS,因为代码更简洁直观。
核心步骤:
- 构建邻接表
- 创建受限节点的哈希集合
- 从节点 0 开始 DFS,计数可达节点
- 返回计数结果
代码实现
class Solution {
public:
int reachableNodes(int n, vector<vector<int>>& edges, vector<int>& restricted) {
// 构建邻接表
vector<vector<int>> graph(n);
for (auto& edge : edges) {
graph[edge[0]].push_back(edge[1]);
graph[edge[1]].push_back(edge[0]);
}
// 标记受限节点
unordered_set<int> restrictedSet(restricted.begin(), restricted.end());
// DFS遍历计数
vector<bool> visited(n, false);
return dfs(graph, restrictedSet, visited, 0);
}
private:
int dfs(vector<vector<int>>& graph, unordered_set<int>& restrictedSet,
vector<bool>& visited, int node) {
visited[node] = true;
int count = 1;
for (int neighbor : graph[node]) {
if (!visited[neighbor] && restrictedSet.find(neighbor) == restrictedSet.end()) {
count += dfs(graph, restrictedSet, visited, neighbor);
}
}
return count;
}
};
class Solution:
def reachableNodes(self, n: int, edges: List[List[int]], restricted: List[int]) -> int:
# 构建邻接表
graph = [[] for _ in range(n)]
for a, b in edges:
graph[a].append(b)
graph[b].append(a)
# 标记受限节点
restricted_set = set(restricted)
# DFS遍历计数
visited = [False] * n
def dfs(node):
visited[node] = True
count = 1
for neighbor in graph[node]:
if not visited[neighbor] and neighbor not in restricted_set:
count += dfs(neighbor)
return count
return dfs(0)
public class Solution {
public int ReachableNodes(int n, int[][] edges, int[] restricted) {
// 构建邻接表
List<int>[] graph = new List<int>[n];
for (int i = 0; i < n; i++) {
graph[i] = new List<int>();
}
foreach (int[] edge in edges) {
graph[edge[0]].Add(edge[1]);
graph[edge[1]].Add(edge[0]);
}
// 标记受限节点
HashSet<int> restrictedSet = new HashSet<int>(restricted);
// DFS遍历计数
bool[] visited = new bool[n];
return Dfs(graph, restrictedSet, visited, 0);
}
private int Dfs(List<int>[] graph, HashSet<int> restrictedSet, bool[] visited, int node) {
visited[node] = true;
int count = 1;
foreach (int neighbor in graph[node]) {
if (!visited[neighbor] && !restrictedSet.Contains(neighbor)) {
count += Dfs(graph, restrictedSet, visited, neighbor);
}
}
return count;
}
}
var reachableNodes = function(n, edges, restricted) {
// 构建邻接表
const graph = Array(n).fill().map(() => []);
for (const [a, b] of edges) {
graph[a].push(b);
graph[b].push(a);
}
// 标记受限节点
const restrictedSet = new Set(restricted);
// DFS遍历计数
const visited = new Array(n).fill(false);
function dfs(node) {
visited[node] = true;
let count = 1;
for (const neighbor of graph[node]) {
if (!visited[neighbor] && !restrictedSet.has(neighbor)) {
count += dfs(neighbor);
}
}
return count;
}
return dfs(0);
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历所有可达节点,最多访问 n 个节点 |
| 空间复杂度 | O(n) | 邻接表存储图结构需要 O(n) 空间,递归调用栈深度最多为 n |
相关题目
. Open the Lock (Medium)
. Minimum Jumps to Reach Home (Medium)