每日两道leetcode

841. 钥匙和房间 - 力扣(LeetCode)

题目

有 n 个房间,房间按从 0 到 n - 1 编号。最初,除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而,你不能在没有获得钥匙的时候进入锁住的房间。

当你进入一个房间,你可能会在里面找到一套 不同的钥匙,每把钥匙上都有对应的房间号,即表示钥匙可以打开的房间。你可以拿上所有钥匙去解锁其他房间。

给你一个数组 rooms 其中 rooms[i] 是你进入 i 号房间可以获得的钥匙集合。如果能进入 所有 房间返回 true,否则返回 false

    示例 1:

    输入:rooms = [[1],[2],[3],[]]
    输出:true
    解释:
    我们从 0 号房间开始,拿到钥匙 1。
    之后我们去 1 号房间,拿到钥匙 2。
    然后我们去 2 号房间,拿到钥匙 3。
    最后我们去了 3 号房间。
    由于我们能够进入每个房间,我们返回 true。

    示例 2:

    输入:rooms = [[1,3],[3,0,1],[2],[0]]
    输出:false
    解释:我们不能进入 2 号房间。

    提示:

    • n == rooms.length
    • 2 <= n <= 1000
    • 0 <= rooms[i].length <= 1000
    • 1 <= sum(rooms[i].length) <= 3000
    • 0 <= rooms[i][j] < n
    • 所有 rooms[i] 的值 互不相同

    思路

    1. 定义一个队列存储钥匙,每访问一个房间就将该房间的钥匙入队,每次拿出队首的一把钥匙,要是对应的房间已经被访问,则继续取,直到钥匙被取完。此时若是所有房间都被访问了,那么就返回true,否则返回false。

    代码实现

    class Solution {
    public:
        bool canVisitAllRooms(vector>& rooms) {
            int cnt = rooms.size()-1, key;
            queue keys;
            if(!rooms[0].empty()) for(int i = 0; i < rooms[0].size(); i++) keys.push(rooms[0][i]);
            if(rooms[0].empty()) rooms[0].push_back(-1);
            else rooms[0][0] = -1;
            while(!keys.empty()) {
                key = keys.front();
                cout << key << endl;
                keys.pop();
                if(rooms[key].empty() || rooms[key][0] != -1) cnt--;
                else continue;
                if(!rooms[key].empty()) for(int i = 0; i < rooms[key].size(); i++) keys.push(rooms[key][i]);
                if(rooms[key].empty()) rooms[key].push_back(-1);
                else rooms[key][0] = -1;
            }
            if(cnt != 0) return false;
            return true;
        }
    };

    复杂度分析

    • 时间复杂度:O(n+keys)。
    • 空间复杂度:最坏空间复杂度O(keys)。

    知识积累

    • 关于for循环的auto的使用:
      • for(auto i : vector)其中auto会拷贝容器内的vector,修改时不会改变原容器当中的值,只会改变拷贝的vector。
      • for(auto& i : vector),i就变成vector的引用,不发生拷贝,一定程度上会减少操作数,但是对i进行修改会修改到原vector。 
    • vector.resize(size,[value])动态指定vector空间的大小,若指定大小小于原大小则删除多余元素,若大于,则会使用value填充,若不指定value则会默认初始化。
    • queue.emplace(x),也是push的一种逻辑,但是push需要先创建对象的副本,再将其移动到容器中,涉及额外的复制及移动操作;而emplace直接在容器中构造对象,没有额外的复制和移动操作。

    官方题解

    1. 深度优先搜索
      1. 官方题解额外定义了一个访问数组,在获取钥匙前先判断是否访问过对应房间而决定是否需要取这个钥匙,一进一出就比我的钥匙入队方法要快了不少了,而且所用的空间最大也就是O(n),因为递归的深度最深就是房间数,钥匙是平行的空间深度。
      2. 时间复杂度是O(n+keys),空间复杂度是O(n)。
      3. 以下是我看完题解后自己仿写的实现:
        class Solution {
        public:
            vector vis;
            int num;
            void dfs(vector >& rooms, int key) {
                vis[key] = true;
                num++;
                for(auto &i : rooms[key]) {
                    if(!vis[i]) dfs(rooms, i);
                }
            }
            bool canVisitAllRooms(vector>& rooms) {
                int n = rooms.size();
                num = 0;
                vis.resize(n);
                dfs(rooms, 0);
                if(num == n) return true;
                return false;
            }
        };

    2. 广度优先搜索
      1. 广度优先搜索的方法类似我的实现,官方代码增加数组做判断避免重复操作,另外queue的push操作也以更高效的emplace操作代替。

    你可能感兴趣的:(leetcode训练,leetcode,算法,职场和发展,c++,数据结构,深度优先,广度优先)