题目要求
给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。
输入描述
第一行包含两个整数 N,M,表示图中拥有 N 个节点,M 条边
后续 M 行,每行包含两个整数 s 和 t,表示图中的 s 节点与 t 节点中有一条路径
输出描述
输出所有的可达路径,路径中所有节点之间空格隔开,每条路径独占一行,存在多条路径,路径输出的顺序可任意。如果不存在任何一条路径,则输出 -1。
注意输出的序列中,最后一个节点后面没有空格! 例如正确的答案是 `1 3 5`,而不是 `1 3 5 `, 5后面没有空格!
输入示例
5 5
1 3
3 5
1 2
2 4
4 5
输出示例
1 3 5
1 2 4 5
思路
首先要知道图应该怎么存储,然后是通过搜索的方法找出所有的边。
图的存储
邻接矩阵
本题有n个节点,节点标号从1开始,为了对其标号和下标,我们申请n+1*n+1这么大的二维数组。
vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0));
输入m个边,构造方式如下:
while(m--) {cin >> s >> t;// 使用邻接矩阵,1表示节点s指向节点tgraph[s][t] = 1;
}
邻接表
采用数组+链表的形式来表示。
vector<list<int>> graph(n + 1); // 邻接表,list为C++里的链表,初始时没有申请空间,随用随取// 输入m个边
while(m--) {cin >> s >> t;// 使用邻接表,表示s —> t是相连的graph[s].push_back(t);
}
深度优先搜索
深搜三部曲:1.确认递归函数、参数, 2.确认终止条件,3.处理目前搜索节点出发的路径
1. 确认递归函数、参数
首先dfs函数一定要存一个图,用来遍历,需要存一个目前我们遍历的节点,定义为x。
还需要存储终点位置n,当x==n时表示到达了终点。
至于 单一路径 和 路径集合(答案)可以放在全局变量。
vector<vector<int>> result; // 收集符合条件的路径
vector<int> path; // 0节点到终点的路径
// x:目前遍历的节点
// graph:存当前的图
// n:终点
void dfs (const vector<vector<int>>& graph, int x, int n) {
2. 确认终止条件
当目前遍历的节点为最后一个节点n的时候,就找到了一条从出发点到终止点的路径。
// 当前遍历的节点x,到达节点n
if (x == n) { // 找到符合条件的一条路径result.push_back(path);return;
}
3. 处理目前搜索节点出发的路径
接下来时走当前遍历节点x的下一个节点。
// 找到节点x指向哪些节点
for (int i = 1; i <= n; ++i) { // 遍历节点x链接的所有节点if (graph[x][i] == 1) { // 找到x指向的节点就是i// 将x所指向的节点加入到单一路径中path.push_back(i);// 进入下一层递归dfs(graph, i, n);// 回溯过程,撤销本次添加节点的操作path.pop_back();}
}
打印结果
注意结尾没有空格
// 输出结果
if (result.size() == 0) cout << -1 << endl;
for (const vector<int> &pa : result) {for (int i = 0; i < pa.size() - 1; ++i) { // 打印到倒数第二个cout << pa[i] << " ";}cout << pa[pa.size() - 1] << endl;
}
本题代码
C++邻接矩阵写法
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> result;
vector<int> path;void dfs(const vector<vector<int>>& graph, int x, int n) {if (x == n) {result.push_back(path);return;}for (int i = 1; i <= n; ++i) {if (graph[x][i] == 1) {path.push_back(i);dfs(graph, i, n);path.pop_back();}}
}int main() {int n, m, s, t;cin >> n >> m;vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0));for (int i = 1; i <= m; ++i) {cin >> s >> t;graph[s][t] = 1;}path.push_back(1); // 注意:无论什么路径都是从节点0(1)出发dfs(graph, 1, n);if (result.size() == 0) cout << -1 << endl;for (const vector<int> &pa : result) {for (int i = 0; i < pa.size() - 1; ++i) {cout << pa[i] << " ";}cout << pa[pa.size() - 1] << endl;}
}
C++邻接表写法
#include <iostream>
#include <vector>
#include <list>
using namespace std;
vector<vector<int>> result;
vector<int> path;void dfs(const vector<list<int>>& graph, int x, int n) {if (x == n) {result.push_back(path);return;}for (int i : graph[x]) {path.push_back(i);dfs(graph, i, n);path.pop_back();}
}int main() {int n, m, s, t;cin >> n >> m;vector<list<int>> graph(n + 1);while (m--) {cin >> s >> t;graph[s].push_back(t);}path.push_back(1); // 注意:无论什么路径都是从节点0(1)出发dfs(graph, 1, n);if (result.size() == 0) cout << -1 << endl;for (const vector<int> &pa : result) {for (int i = 0; i < pa.size() - 1; ++i) {cout << pa[i] << " ";}cout << pa[pa.size() - 1] << endl;}
}
Python邻接矩阵写法
def dfs(graph, x, n, path, result):if x == n:result.append(path.copy())returnfor i in range(1, n+1):if graph[x][i] == 1:path.append(i)dfs(graph, i, n, path, result)path.pop()def main():n, m = map(int, input().split())graph = [[0] * (n+1) for _ in range(n+1)]for _ in range(m):s, t = map(int, input().split())graph[s][t] = 1result = []dfs(graph, 1, n, [1], result)if not result:print(-1)else:for path in result:print(' '.join(map(str, path)))if __name__ == "__main__":main()
Python邻接表写法(注意collections.defaultdict的使用)
from collections import defaultdictresult = []
path = []def dfs(graph, x, n, path, result):if x == n:result.append(path.copy())returnfor i in graph[x]:path.append(i)dfs(graph, i, n, path, result)path.pop()def main():n, m = map(int, input().split())graph = defaultdict(list)for _ in range(m):s, t = map(int, input().split())graph[s].append(t)result = []dfs(graph, 1, n, [1], result)if not result:print(-1)else:for pa in result:print(' '.join(map(str, pa)))if __name__ == "__main__":main()
Leetcode 797. All Paths From Source to Target
题目也是一道有向无环的图的求所有起点到终点的路径问题,区别在于不需要调整输入输出。(题目中的图已经给出了邻接表的存储形式)
C++
class Solution {
public:vector<int> path;vector<vector<int>> result;void dfs(const vector<vector<int>>& graph, int x, int n) {if (x == n) {result.push_back(path);return;}for (int i : graph[x]) {path.push_back(i);dfs(graph, i, n);path.pop_back();}}vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {path.push_back(0);dfs(graph, 0, graph.size() - 1);return result; }
};
Python (注意列表是引用类型,后续修改会影响已经添加的路径,所以要用.copy())
class Solution:def dfs(self, graph, x, n, path, result):if (x == n):result.append(path.copy())returnfor i in graph[x]:path.append(i)self.dfs(graph, i, n, path, result)path.pop()def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:path = []result = []path.append(0)self.dfs(graph, 0, len(graph) - 1, path, result)return result