【C++BFS】802. 找到最终的安全状态

本文涉及知识点

C++BFS算法

LeetCode802. 找到最终的安全状态

有一个有 n 个节点的有向图,节点按 0 到 n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示, graph[i]是与节点 i 相邻的节点的整数数组,这意味着从节点 i 到 graph[i]中的每个节点都有一条边。
如果一个节点没有连出的有向边,则该节点是 终端节点 。如果从该节点开始的所有可能路径都通向 终端节点 ,则该节点为 安全节点 。
返回一个由图中所有 安全节点 组成的数组作为答案。答案数组中的元素应当按 升序 排列。

示例 1:

在这里插入图片描述

输入:graph = [[1,2],[2,3],[5],[0],[5],[],[]]
输出:[2,4,5,6]
解释:示意图如上。
节点 5 和节点 6 是终端节点,因为它们都没有出边。
从节点 2、4、5 和 6 开始的所有路径都指向节点 5 或 6 。
示例 2:

输入:graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]]
输出:[4]
解释:
只有节点 4 是终端节点,从节点 4 开始的所有路径都通向节点 4 。

提示:

n == graph.length
1 <= n <= 104
0 <= graph[i].length <= n
0 <= graph[i][j] <= n - 1
graph[i] 按严格递增顺序排列。
图中可能包含自环。
图中边的数目在范围 [1, 4 * 104] 内。

C++BFS

本题本质是拓扑排序,如果有相应的模板,则用模板,否则用BFS。
预处理:graph记录的是后续的节点,计算vPre前续节点。
BFS的状态:leves[0]记录所有终端节点,leves[i]记录所有边都指向leves[0…i-1]的节点。空间复杂度:O(n)。
BFS的后续状态:cur所有前置节点中,扣掉指向leves[0…i-1]后,出度为0的节点。时间复杂度:O(m),m是边数。注意:不能有重边。
BFS的初始状态:所有终端节点。
BFS的返回值:用一维数组代替二维数组,并排序。
BFS的重复处理:数组出重。

代码

核心代码

class Solution {
		public:
			vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
				const int N = graph.size();
				vector<vector<int>> vPre(N);
				vector<int> vOut(N);
				for (int i = 0; i < graph.size(); i++) {
					for (const auto& next : graph[i]) {
						vPre[next].emplace_back(i);
					}
					vOut[i] = graph[i].size();
				}
				vector<bool> vis(N);
				
				vector<int> v;
				auto Add = [&](int node) {
					if (vis[node]) { return; }
					if (0 != vOut[node]) { return; }	
					v.emplace_back(node);
					vis[node] = true;
				};
				for (int i = 0; i < graph.size(); i++) {
					if(graph[i].empty()){
						Add(i);
					}
				}
				for (int i = 0; i < v.size(); i++) {
					for (const int ip : vPre[v[i]]) {
						vOut[ip]--;
						Add(ip);
					}
				}
				sort(v.begin(), v.end());
				return v;
			}
		};

单元测试

		TEST_METHOD(TestMethod1)
		{
			graph = { {} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{0}, res);
		}
		TEST_METHOD(TestMethod2)
		{
			graph = { {0} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{}, res);
		}
		TEST_METHOD(TestMethod3)
		{
			graph = { {},{} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{0,1}, res);
		}
		TEST_METHOD(TestMethod4)
		{
			graph = { {1},{} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{0, 1}, res);
		}
		TEST_METHOD(TestMethod5)
		{
			graph = { {},{0} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{0, 1}, res);
		}
		TEST_METHOD(TestMethod6)
		{
			graph = { {1},{0} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{}, res);
		}
		TEST_METHOD(TestMethod11)
		{
			graph =  { {1,2},{2,3},{5},{0},{5},{},{} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{2,4,5,6}, res);
		}
		TEST_METHOD(TestMethod12)
		{
			graph = { {1,2,3,4},{1,2},{3,4},{0,4},{} };
			auto res = Solution().eventualSafeNodes(graph);
			AssertEx(vector<int>{4}, res);
		}		


如果有不明白的,请加文末QQ群。如果要打包下载源码(CSDN下载频道偶尔审核不通过,原因未知),也请加QQ群。

扩展阅读

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等

课程
https://edu.csdn.net/lecturer/6176

相关推荐

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本**

算法C++**实现。

  • 149
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 137
    评论
以下是八数码问题的BFS算法C++实现: ```c++ #include <iostream> #include <queue> #include <unordered_map> #include <vector> using namespace std; const int N = 3; // 3*3的方格 const int SIZE = N * N; // 每个状态的大小 const int dx[4] = {-1, 0, 1, 0}; // 四个方向的x坐标变化 const int dy[4] = {0, 1, 0, -1}; // 四个方向的y坐标变化 struct State { int board[N][N]; // 当前状态的棋盘 int zeroX, zeroY; // 0的位置 int steps; // 走到当前状态的步数 State() { steps = 0; zeroX = zeroY = -1; memset(board, 0, sizeof(board)); } // 初始化 bool operator==(const State &that) const { // 判断两个状态是否相等 for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (board[i][j] != that.board[i][j]) return false; return true; } bool operator!=(const State &that) const { return !(*this == that); } }; struct Hash { size_t operator()(const State &state) const { // 哈希函数 size_t hash = 0; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) hash = hash * 10 + state.board[i][j]; return hash; } }; void printPath(const vector<State> &path) { // 输出路径 for (const auto &state : path) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) cout << state.board[i][j] << " "; cout << endl; } cout << endl; } } int bfs(const State &start, const State &end) { queue<State> q; unordered_map<State, int, Hash> dist; // 记录状态到起点的距离 unordered_map<State, State, Hash> parent; // 记录状态的父亲 State curr = start; dist[curr] = 0; // 起点到起点的距离为0 q.push(curr); while (!q.empty()) { curr = q.front(); q.pop(); if (curr == end) { // 找到最终状态 vector<State> path; while (curr != start) { path.push_back(curr); curr = parent[curr]; } path.push_back(start); reverse(path.begin(), path.end()); // 反转路径 printPath(path); return dist[end]; // 返回最短距离 } int x = curr.zeroX, y = curr.zeroY; for (int i = 0; i < 4; i++) { // 四个方向扩展 int nx = x + dx[i], ny = y + dy[i]; if (nx >= 0 && nx < N && ny >= 0 && ny < N) { // 判断是否越界 State next = curr; swap(next.board[x][y], next.board[nx][ny]); // 交换0和相邻位置的数 next.zeroX = nx, next.zeroY = ny; // 更新0的位置 if (dist.count(next) == 0) { // 判断是否已经扩展过 dist[next] = dist[curr] + 1; parent[next] = curr; q.push(next); } } } } return -1; // 无法到达最终状态 } int main() { State start, end; cout << "请输入初始状态(共" << SIZE << "个数):" << endl; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) { cin >> start.board[i][j]; if (start.board[i][j] == 0) start.zeroX = i, start.zeroY = j; } cout << "请输入最终状态(共" << SIZE << "个数):" << endl; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) { cin >> end.board[i][j]; if (end.board[i][j] == 0) end.zeroX = i, end.zeroY = j; } int steps = bfs(start, end); if (steps == -1) cout << "无法到达最终状态" << endl; else cout << "最少移动次数为:" << steps << endl; return 0; } ``` 该算法使用了哈希表来记录状态,从而避免了重复计算。同时,使用了unordered_map来记录状态到起点的距离和状态的父亲,从而在找到最终状态后可以输出路径。
评论 137
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值