dfs

深度优先搜索解决钥匙与房间问题
问题介绍
力扣上的第 841 题 "钥匙和房间" 是一个非常经典的图遍历问题。题目描述如下:
有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0, 1, 2, ..., N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i 都有一个钥匙列表 rooms [i],每个钥匙 rooms [i][j] 由 [0,1,...,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms [i][j] = v 可以打开编号为 v 的房间。
最初,除 0 号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入所有房间返回 true,否则返回 false。
例如:
输入: [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。
深度优先搜索原理
深度优先搜索(DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点 v 的所在边都己被探寻过,搜索将回溯到发现节点 v 的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。
在本题中,我们可以将每个房间看作图中的一个节点,房间中的钥匙看作节点之间的有向边。这样,问题就转化为从节点 0 出发,能否遍历到图中的所有节点。
深度优先搜索的实现通常有两种方式:递归和迭代。递归实现更加直观,因此我们选择使用递归来解决这个问题。
代码实现
下面是使用深度优先搜索解决该问题的代码实现:
cpp
class Solution {
public:
vector vis;
int num;

void dfs(vector<vector>& rooms, int x) {
vis[x] = true;
num++;
for (auto& it : rooms[x]) {
if (!vis[it]) {
dfs(rooms, it);
}
}
}

bool canVisitAllRooms(vector<vector>& rooms) {
int n = rooms.size();
num = 0;
vis.resize(n);
dfs(rooms, 0);
return num == n;
}
};
代码解释
数据结构
vector vis:用于记录每个房间是否被访问过
int num:记录已经访问过的房间数量
核心函数
dfs(vector<vector>& rooms, int x):从房间 x 开始进行深度优先搜索
将当前房间 x 标记为已访问
增加已访问房间的计数
遍历当前房间 x 中的所有钥匙
如果钥匙对应的房间未被访问,则递归访问该房间
主函数
bool canVisitAllRooms(vector<vector>& rooms):判断是否可以访问所有房间
获取房间总数 n
初始化已访问房间计数 num 为 0
调整 vis 数组大小为 n
从房间 0 开始进行深度优先搜索
判断已访问房间数是否等于房间总数
复杂度分析
时间复杂度:O (n + m),其中 n 是房间的数量,m 是所有房间中的钥匙数量的总和。这是因为我们需要遍历每个房间和每个钥匙一次。
空间复杂度:O (n),主要用于存储访问标记数组和递归调用栈的空间。
算法优化思考
这个问题还可以使用广度优先搜索(BFS)来解决,通过队列来替代递归调用栈。广度优先搜索在某些情况下可能会更高效,特别是当图的分支因子较大时,可以避免递归调用栈过深导致的栈溢出问题。
另外,如果题目中的房间数量非常大,我们还可以考虑使用并查集(Union-Find)来解决这个问题,不过并查集在判断连通性方面更适合无向图,对于本题的有向图需要做一些特殊处理。
实际应用场景
这个问题实际上模拟了很多现实生活中的场景,比如:
游戏中的关卡解锁系统
网络中的节点访问权限
文件系统中的目录访问权限
社交网络中的用户关系和内容访问
通过解决这个问题,我们可以学习到如何将现实世界中的问题抽象为图论问题,并用适当的算法来解决它们。
总结
深度优先搜索是解决图遍历问题的一种强大工具,它能够有效地判断从一个节点出发是否能够到达图中的所有其他节点。在解决问题时,我们需要根据问题的特点选择合适的数据结构和算法,同时还要考虑算法的时间和空间复杂度。
希望通过这篇博客,你对深度优先搜索有了更深入的理解,并且能够熟练运用它来解决类似的问题。

posted @ 2025-05-13 23:07  林宇钦  阅读(142)  评论(0)    收藏  举报