前端刷题路-Day93:钥匙和房间(题号841)

389 阅读1分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

钥匙和房间(题号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:

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

示例 2:

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

提示:

  • 1 <= rooms.length <= 1000
  • 0 <= rooms[i].length <= 1000
  • 所有房间中的钥匙数量总计不超过 3000

链接

leetcode-cn.com/problems/ke…

解释

这题啊,这题是经典有向图。

什么是有向图?其实就是有方向的图,这里的方向就是从数组中的A元素可以定位到B元素,也就是题目里的钥匙。

其实这就是单纯一概念而已,即使不知道对解出这题也没什么影响的,笔者在解的时候也不知道这个概念,还是写出来BFS和DFS两种解法,问题不大。

回到题目,这题的题意就是可以从数组的A元素拿到一些值,这些值代表着可以去的另外一些元素,以此类推,如果可以走到数组的每个元素,就返回true,否则返回false

既然题目要确定每个房子都可以走到,那么第一时间想到的就是一个哈希表,用这个表来记录当前到过的房子,或者说是数组中的元素,有个这个哈希表之后,直接按照元素的顺寻开始迭代即可。

这里需要另外一个新的数组来存储需要迭代的值,这里简单就叫arr吧,一开始arr是有默认值的,也就是数组的第一个元素,根据题意,0号房间的门是开着的,所以所以arr的默认值就是0号房间的值,同时哈希表的初始化值也就是0号房间了。

完事之后使用while来遍历arr中的元素,拿到新房间的钥匙,如果这个房间在哈希表里已经有了,那就不做任何操作,连该房间的值也不用推入到arr中,如果没有的话,将当前房间的index放到哈希表里面,证明该房间已经走过了,并且将该房间的值推入到arr数组中去,方便后续进行下一次迭代。

这就是简单的BFS解法,比较简单,也好理解,缺点就是比较占用存储空间吧,需要存储数组中的所有元素和所有元素的值。

再说说DFS,递归的方法可以说是和迭代的没什么区别,用一个函数来迭代没一个房子的每一个值,如果该房间走过,那就直接return,否则一次递归里面的每个值。

DFS也可以使用哈希表的方式来解决,但其实这里可以有个小优化,这里放到题解上说。

自己的答案(BFS)

var canVisitAllRooms = function(rooms) {
    const set = new Set([0])
    let arr = rooms[0]
    while (arr.length) {
      const active = arr.pop()
      if (!set.has(active)) {
        set.add(active)
        arr = arr.concat(rooms[active])
      }
    }
    return set.size === rooms.length
};

迭代就是这样了,size里面统计的是已经走过的房子,如果它个所有房子的个数相等,那就证明所有的房子都可以走,否则就证明有的房子每走过,返回false

自己的答案(DFS)

来到递归这里,递归可以使用哈希表来解决问题,但哈希表不是占空间么,这里可以使用一种不占空间的方法,直接修改rooms数组。

把每个走过的房子都设置为null,这样如果第二次走到这个房子是null,即可直接返回false,停止递归,如果不是null,那就进行递归操作,同时将的rooms中的当前值设为null

这里是第一个优化点,第二个优化点是不用存储每个房间的房间号了,根据题意,我们只需要统计房间的个数即可,那每次遇到一个新房间,给房间数的个数自增一是不就好了,直接用最后的房间数和rooms的长度进行比较,如果不一样就GG。

var canVisitAllRooms = function(rooms) {
   let sum = 0
    function DFS(i) {
      if (!rooms[i]) return
      const item = rooms[i].slice(0)
      sum++
      rooms[i] = null
      for (const v of item) {
        DFS(v)
      }
    }
    DFS(0)
    return sum === rooms.length
};

看起来代码稍微长了一点,但实际跑起来却会快不少,一方面可能是因为递归就是比迭代快,另一方面可能就是减少了哈希表的存储吧~

更好的方法



PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的👇

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇

前端刷题路-目录(题型分类)