图的遍历 深度与广度优先遍历

图的遍历

本文作者 Mr.赵

Email: lovelyalan@foxmail.com

原创文章,转载标明作者,部分图片素材来自网络

从图中某一顶点出发遍历图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历。


本文暂未代入各个编程语言的实现代码,主要目的为理解原理


一.深度优先遍历(DFS)

1.概念

一直向前冲,直到世界尽头


从图中一个未访问的顶点 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底…,不断重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。

2.动图演示

此处需要你有 栈(Stack) 基础

dfs

可以看到,网页模拟了深度搜索二叉树的动态变化过程:

  • 二叉树中当前遍历到的节点会变成红色;
  • 栈中压入 (push)的节点为灰色;
  • 栈中弹出 (pop) 的节点会变为红色, 然后消失;

二.广度优先遍历(BFS)

1.概念

如涟漪向外扩散


指的是从图的一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历每个相邻节点的相邻节点。

2.动图演示

此处需要你有 队列 基础

bfs


bfs2

可以看到,网页模拟了广度搜索二叉树队列的动态变化过程:

  • 二叉树中当前遍历到的节点会变成黄色;
  • 队列中入队 (put)的节点为黄色;
  • 队列中出队 (take) 的节点背景是 蓝色, 代表已经遍历过的值;

三.总结

1.相同点

我们不难发现**,BFS 先遍历了2和4**,才去遍历了2下面的5和6

DFS就会 遍历2->5->6再去遍历4

2.不同点

深度搜索会在一条路走到尽头,然后返回分叉口,在新的路再走到尽头,循环此过程,直到没有分叉口可走了,代码实现起来比较简单

深度优先就是,从初始点出发,不断向前走,如果碰到死路了,就往回走一步,尝试另一条路,直到发现了目标位置。这种不撞南墙不回头的方法,即使成功也不一定找到一条好路,但好处是需要记住的位置比较少


广度搜索每次会在每个分叉口后面的路都先走一步,然后接着向前走,有了新的分叉口就同理,直到所有路都走完了,代码实现起来比较困难.

广度搜索属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图直到找到结果为止

3.总结

效率: 深度优先搜索遍历的位置比较少,而广度优先搜索会遍历所有位置,所以效率上肯定是深度优先搜索更胜一筹


准确: 在有多个结果的情况下,深度优先搜索可能返回的结果不是最优解,广度优先搜索因为得到了所有的数据,可以结果最优解上广度优先搜索更胜一筹

没有最好的算法,只有合适的场景,适当的选择适合自己使用场景的算法即可.

四.案例实战

1.题目

来自 LeetCode


钥匙和房间

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。

2.代码实现

这里是Java的,其他语言爱好者可以 LeetCode 的自己做一下

class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        boolean[] seen = new boolean[rooms.size()];//记录每一个房间是否开启
        seen[0] = true;//题目中说 0 号房间是打开的
        Stack<Integer> stack = new Stack();//栈
        stack.push(0);//用0做头节点
        
        while (!stack.isEmpty()){//只要还有路走就不停下来
            for (int x : rooms.get(stack.pop())){//遍历这条路下的每一个节点
                if (!seen[x]){//如果这个节点在seen数组中为false
                    seen[x]=true;//就更新它的值
                    stack.push(x);//并且作为一个新的分叉路存到栈中
                }
            }
        }

        for (int i = 0; i < seen.length; i++) {//如果这个存房间是否开启的数组中有一间是关闭的 就结束程序并返回false
            if (!seen[i])return false;
        }
        return true;//如果程序没有被上面的for给结束掉,就说明它所有的门都打开了
    }
}

思维泡泡:

你能想到这是 深度 还是 广度吗?

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值