图的深度优先遍历:DFS遍历

2023-11-12

图的深度优先遍历:DFS遍历

提示:系列图的文章
提示:大厂笔试面试都可能不咋考的数据结构:图

由于图的结构比较难,出题的时候,很难把这个图的数据搞通顺,而且搞通顺了题目也需要耗费太多时间,故笔试面试都不会咋考
笔试大厂考的就是你的贪心取巧策略和编码能力,这完全不必用图来考你,其他的有大量的选择
面试大厂考你的是优化算法的能力,但是图没啥可以优化的,只要数据结构统一,它的算法是固定死的,所以不会在面试中考!
万一考,那可能都是大厂和图相关的业务太多,比如美团高德地图啥的,这种考的少。

但不管考不考,我们要有基础,要了解图的数据结构和算法。万一考了呢,准备以备不时之需。

图的数据结构比较难,算法是固定的套路
咱们需要统一一种自己熟悉的图的数据结构,方便套用算法时好写!!

下面是咱们得关于图的重要基础知识和重点应用:
(1)图数据结构,图的统一数据结构和非标准图的转化算法
(2)图的宽度优先遍历:BFS遍历


图的深度优先遍历和二叉树的深度优先类似,只不过多了分叉

看看二叉树的先序遍历非递归实现DFS:
二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现

二叉树的dfs分为先序、中序和后序
咱们只说先序非递归实现方法:dfs

实际上就是用栈,逆序的功能来控制头左右打印输出

//复习(1)如果遇到x打印,然后先压右子,再压左子,弹出时,必定先弹出左子,再弹出右子
    //这不就是头、左、右吗?——先序遍历
    public static void unRecurrentPrePrint(Node head){
        if (head == null) return;

        Stack<Node> stack = new Stack<>();
        stack.push(head);//先压头
        while (!stack.isEmpty()){
            //首先打印头
            Node cur = stack.pop();
            System.out.print(cur.value + " ");
            //然后反着压,先压右边,再压左边,回头就是左右顺序出
            if (cur.right != null) stack.push(cur.right);
            if (cur.left != null) stack.push(cur.left);
        }
    }

图的深度优先遍历DFS:要理解透彻

咱们图的BFS和二叉树的BFS一样,
但是图的DFS跟二叉树的DFS并不完全一样,咱们要的不是逆序功能
咱要的是让栈记住沿途我走过的路径,方便回来之后找到旁支继续深度遍历
一条道走到黑 !关键在两点:cur的邻居没遍历过的话, 再次压入cur ,只加一个邻居到栈里面, 立马break

具体咋实现呢?
一定要深刻理解一条道走到黑是如何用栈实现的
(1)head=1进栈,第一次压入时打印,后面压邻居才打印,set记录head(代表已经遍历过了);
(2)cur=栈弹出的1,看cur的直接邻居,没进去过的,只要一个next=2,先将cur 再次压入 ,记住这条路,然后压入next=2,压入打印,set记录next,立马break
(3)cur=栈弹出的2,看cur的直接邻居,没进去过的,只要一个next=3,先将cur 再次压入,记住这条路,然后压入next=3,压入打印,set记录next,立马break
(4)cur=栈弹出的3,看cur的直接邻居,没进去过的,只要一个next=5,先将cur 再次压入,记住这条路,然后压入next=5,压入打印,set记录next,立马break
(5)cur=栈弹出的5,看cur的直接邻居,自己邻居都进去过,不管;
(6)cur=栈弹出的3,看cur的直接邻居,自己邻居都进去过,不管;
(7)cur=栈弹出的2,看cur的直接邻居,自己邻居都进去过,不管;
(8)cur=栈弹出的1,看cur的直接邻居,没进去过的,只要一个next=4,先将cur 再次压入,记住这条路,然后压入next=4,压入打印,set记录next,立马break
(9)cur=栈弹出的4,看cur的直接邻居,自己邻居都进去过,不管;
(10)cur=栈弹出的1,看cur的直接邻居,自己邻居都进去过,不管;
(11)栈空,结束
在这里插入图片描述
看见没,上面依次访问了 1 2 3 5 然后回去 5 3 2 1,然后才去的4
这就是一条道走到黑,通过栈,再次压入cur记住自己来过的路径,压入邻居打印邻居,然后立马break,保证直走一条道
这就是图的DFS原理,遍历过得点加入set,等整体栈为空,OK。

手撕代码,搞懂了这个流程,就非常非常简单了

//复习图的dfs
    public static void dfsReview(Node head){
        if (head == null) return;

        //一定要深刻理解一条道走到黑是如何用栈实现的
        Stack<Node> stack = new Stack<>();
        Set<Node> set = new HashSet<>();
        //(1)head=1进栈,第一次压入时打印,后面压邻居才打印,set记录head(代表已经遍历过了);
        stack.push(head);
        System.out.print(head.value +" ");
        set.add(head);
        while (!stack.isEmpty()){
            //(2)cur=栈弹出的**1**,看cur的直接邻居,没进去过的,只要一个next=2,
            Node cur = stack.pop();
            for(Node next:cur.nexts){
                if (!set.contains(next)){
                    // 先将cur再次压入,记住这条路,然后压入next=2,压入打印,set记录next,立马break;
                    stack.push(cur);
                    stack.push(next);
                    System.out.print(next.value +" ");
                    set.add(next);
                    break;//立马退出循环
                }
            }
        }
    }

验证一下:
生成一个统一的丰富图结构:

//基础的数据类型,得自个写
    //边--和节点是相互渗透的俩数据结构
    public static class Edge{
        public int weight;
        public Node from;
        public Node to;
        public Edge(int w, Node a, Node b){
            weight = w;
            from = a;
            to = b;
        }
    }

    //节点
    public static class Node{
        public int in;
        public int out;
        public int value;
        public ArrayList<Node> nexts;
        public ArrayList<Edge> edges;//这里是相互渗透的,既然有邻居,就右边

        public Node(int v){
            value = v;//只需要给这么一个value即可
            in = 0;
            out = 0;
            nexts = new ArrayList<>();
            edges = new ArrayList<>();
        }
    }

    //图结构玩起来,图右边,节也有边
    public static class Graph{
        public HashMap<Integer, Node> nodes;//v,Node
        public HashSet<Edge> edges;
        public Graph(){
            nodes = new HashMap<>();//一般节点有value,对应包装袋,都是用哈希表玩的,并查集就是这么玩的
            edges = new HashSet<>();
        }

        public int getNodeNum(){
            return nodes.size();
        }

        public int getEdgeNum(){
            return edges.size();
        }
    }

    //将非标准图结构转化为左神标准图结构
    public static Graph generatGrap(int[][] matrix){
        if (matrix == null || matrix.length == 0) return null;
        //matrix==
        //[1,1,2]
        //[2,2,3]
        //[3,3,1],w,f,t
        //挨个遍历行
        Graph graph = new Graph();

        for (int i = 0; i < matrix.length; i++) {
            //建节点和边,然后装图,将节点的入度,出度,边和邻居放好
            int weight = matrix[i][0];
            int from = matrix[i][1];
            int to = matrix[i][2];

            //图中没节点,建,否则不必重复搞
            if (!graph.nodes.containsKey(from)) graph.nodes.put(from, new Node(from));
            if (!graph.nodes.containsKey(to)) graph.nodes.put(to, new Node(to));

            Node fromNode = graph.nodes.get(from);
            Node toNode = graph.nodes.get(to);//有就拿出来

            Edge edge = new Edge(weight, fromNode, toNode);//建边
            graph.edges.add(edge);

            fromNode.out++;
            toNode.in++;
            fromNode.nexts.add(toNode);
            fromNode.edges.add(edge);//除了入度说终点,其余都是说原点
        }

        return graph;
    }
public static void test(){
        int[][] matrix = {
                {1,1,2},
                {2,2,3},
                {3,3,5},
                {4,2,5},
                {5,1,3},
                {5,1,4}
        };
        Graph graph = generatGrap(matrix);
        System.out.println("bfs:");
        bfs(graph.nodes.get(1));//第一个节点开始遍历打印
        System.out.println("\n复习bfs:");
        bfsReview(graph.nodes.get(1));//第一个节点开始遍历打印


        System.out.println("\ndfs:");
        dfs(graph.nodes.get(1));//第一个节点开始遍历打印
        System.out.println("\n复习dfs:");
        dfsReview(graph.nodes.get(1));//第一个节点开始遍历打印

    }

在这里插入图片描述

bfs:
1 2 3 4 5 
复习bfs:
1 2 3 4 5 
dfs:
1 2 3 5 4 
复习dfs:
1 2 3 5 4 

前面那图的bfs也自己复习一下,非常非常简单的。


总结

提示:重要经验:

1)图的深度优先遍历dfs,思想的核心与二叉树的深度优先遍历有区别,主要是控制一条道走到黑,通过再次压入cur和压未被遍历过的邻居之后立马break来控制非常非常强大。
2)图的宽度优先遍历bfs用队列实现,图的深度优先遍历dfs用栈来实现,如果将来有人为你,如何用栈实现bfs,如何用队列实现dfs,它考你的不是真的bfs和dfs哦!而是考你如何用队列实现栈,如何用栈实现队列,再用你自己实现的队列去做bfs,用你自己实现的栈去实现dfs,可不就是【如何用栈实现bfs,如何用队列实现dfs】的本意吗?
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图的深度优先遍历:DFS遍历 的相关文章

  • 基于Java的迷宫小游戏

    一 实验内容 1 迷宫游戏是非常经典的游戏 在该题中要求随机生成一个迷宫 并求解迷宫 2 要求查找并理解迷宫生成的算法 并尝试用两种不同的算法来生成随机的迷宫 要求迷宫游戏支持玩家走迷宫 和系统走迷宫路径两种模式 玩家走迷宫 通过键盘方向键
  • Knight Moves_dfs_2018_3_10

    A friend of you is doing research on the Traveling Knight Problem TKP where you are to find the shortest closed tour of
  • Acwing 93. 递归实现组合型枚举

    u n start lt m 为剪枝操作 当前已选择的数 剩下的数 凑不出指定的个数m 直接return include
  • 树形dp(例题)

    树的最长路径带权值 树的直径可能时红色的边 从上图可以看出 每次要两个变量存放以u为根 最长路径d1 和次长路径d2 那么整个树的最长路径就有可能是d1 d2 我们每次要返回以u为根的贯穿试的最长路径 给他的父节点判断使用如下图 inclu
  • 16-DFS(深度优先搜索算法)

    DFS 深度优先算法 是常见的搜索算法 早期常用于需要搜索的地方 并且还拓展出很多其它算法 深度优先算法 DFS DFS 深度优先算法 是早期开发爬虫时常用的算法 它的搜索思路是从树根开始一直找直到找到树型数据结构的叶节点 以搜索一个节点数
  • 【华为机试真题 Python】统计每个月兔子的总数

    目录 题目描述 输入 输出 示例 参考代码 题目描述 有一种兔子 从出生后第3个月起每个月都生一只兔子 小兔子长到第三个月后每个月又生一只兔子 例子 假设一只兔子第3个月出生 那么它第5个月开始会每个月生一只兔子 一月的时候有一只兔子 假如
  • AcWing 378. 骑士放置(最大独立集&&匈牙利算法)

    输入样例 2 3 0 输出样例 4 解析 题意为求最大独立集 即为总点数 最小点覆盖 include
  • 链式前向星存树图和遍历它的两种方法【dfs、bfs】

    目录 一 链式前向星存图 二 两种遍历方法 一 链式前向星存图 n个点 n 1条边 链式前向星把上面的树图存下来 输入 9 代表要存进去n个点 1 2 下面是n 1条边 每条边连接两个点 1 3 1 7 2 4 4 5 4 6 3 8 3
  • 每天进步一点点【图的深度优先搜索与广度优先搜索】

    图是一种数据结构 其中节点可以具有零个或多个相邻元素 两个节点之间的连接称为边 节点也可以称为顶点 图分为三种 无向图 有向图 带权图 图的表示方式有两种 二维数组表示 邻接矩阵 链表表示 邻接表 邻接矩阵 邻接矩阵是表示图形中顶点之间相邻
  • Fix a Tree【Codeforces 699 D】【dfs + 树的性质】

    Codeforces Round 363 Div 2 D 题意 有N个点 每个点i都有一个父节点p i 如果 i p i 则是说明i结点是根结点 现在我们给出这样的1 N的p i 这可能是不合法的 问 我们应该最少改变多少个使它变成一棵合法
  • 仙岛求药 —— dfs 与 bfs求解

    样例输入1 8 8 样例输出1 10 样例输入2 9 6 样例输出2 1 dfs ps 使用dfs会运行超时 30组测试数据只能通过部分 其实这种最短路径 最少操作的问题最好还是靠bfs解决 import java util Scanner
  • 【网格问题】leetcode1020.飞地的数量

    题目 给你一个大小为 m x n 的二进制矩阵 grid 其中 0 表示一个海洋单元格 1 表示一个陆地单元格 一次 移动 是指从一个陆地单元格走到另一个相邻 上 下 左 右 的陆地单元格或跨过 grid 的边界 返回网格中 无法 在任意次
  • Max Flow P

    Max Flow P 题目传送门 题目大意 题目大意就是给你一棵树 再给你K次操作将x到y的所有点的值都加1 然后输出所有点值的最大值 思路 这一题如果用暴力的话从范围来看肯定会T tle 所以我们要考虑用差分的思想去做 代码 先看代码 i
  • 图的遍历(深度优先遍历,DFS)

    1 概念 图的遍历操作是从图中某一顶点出发 对图中所有顶点访问一次且仅访问一次 1 在图中 遍历的起始顶点是编号最小的顶点 2 某个起点到达不了所有顶点 则多次调用访问所有顶点 3 为避免遍历因回路而陷入死循环 附设置访问标志数组visit
  • 每日写题分享--机器人的运动范围//DFS深度优先搜索/递归

    题目描述 题目链接戳此 解题思路 这题和上题矩阵中的路径可以对比起来看 同样也是深度优先搜索 DFS 由于机器人从 0 0 位置向下向右探索 右边的下面和下面的右边可能会重复 所以可以将走过的路径记录下来置为true防止重复走 代码实现如下
  • 深度遍历 和 广度遍历

    深度dfs 用到了递归 先把根节点 输出根节点 然后遍历根节点的孩子 const fun1 root gt console log root val root children forEach fun1 fun1 tree 广度遍历 每次遍
  • 基于振动传感器数据构建预测性维护AI模型

    预测性维修 Predictive Maintenance 简称PdM 是以状态为依据 Condition Based 的维修 在机器运行时 对它的主要 或需要 部位进行定期 或连续 的状态监测和故障诊断 判定装备所处的状态 预测装备状态未来
  • DFS的个人理解和测试例题

    深度优先搜索 DFS 是一种搜索手段 可以理解为 它从某个位置 起点 开始 沿着一条路不断地向前走直到尽头 然后退后一步 去走其它没走过的路 没有的话 再退后一步 再去选择 直到找到目的地 终点 例如下图 从A 起点 开始走 先走ABD 在
  • 邻接表表示图进行深度优先搜索,广度优先搜索,最小生成树

    图的邻接表定义 下面用邻接表实现图的深度优先搜索和广度优先搜索 用邻接矩阵来实现最小生成树 图的邻接表 首先定义一个图的邻接表的类 里面包括图的顶点数 图的边数 顶点表数组 由于顶点表数组里存放的都是图的一个个节点 因此需要用到顶点节点和边
  • 冒泡排序/选择排序/插入排序/快速排序/归并排序/桶排序/堆排序/希尔排序/计数排序/基数排序/二分查找/广度优先搜索/深度优先搜索

    排序算法 冒泡排序 Bubble Sort 通过重复地比较相邻的元素并交换它们 使得最大 或最小 的元素逐渐移动到列表的一端 从而实现排序 选择排序 Selection Sort 在未排序的部分中 选择最小 或最大 的元素 并将其放置在已排

随机推荐

  • linux启动时有文件错误,Linux 无法启动常见的几种原因及解决办法

    导致 Linux 无法启动的原因有很多 下面良许小编就将常见的几种原因及解决办法进行详述 希望对大家有所帮助 文件系统配置不当 如 etc inittab文件 etc fstab 文件等配置错误或丢失 导致系统出现故障 以至于无法启动 非法
  • 应急响应-钓鱼邮件的处理思路溯源及其反制

    0x00 钓鱼邮件的危害 1 窃取用户敏感信息 制作虚假网址 诱导用户输入敏感的账户信息后记录 2 携带病毒木马程序 诱导安装 使电脑中病毒木马等 3 挖矿病毒的传输 勒索病毒的传输等等 0x01 有指纹的钓鱼邮件的溯源处理 从邮件中获取相
  • buildroot添加新硬件内核支持

    1
  • 对开发来讲,业务重要还是技术重要?

    很多开发者为天天写业务代码无暇提升技术而焦虑 苦恼 比如 又如 又如 再如 那么 作为开发者 到底该怎么面对 写业务代码 这件事呢 今天我们就从以下几个方面聊聊这个话题 什么是业务 业务和技术的关系 业务和因解决业务而衍生的业务 对业务的态
  • Bitbucket代码迁移到Gitlab

    首先需要确定使用具有一定权限的账号进行迁移 然后在迁移的机器上配置git环境 添加账户信息 git config global user name XXX git config global user email XXX XXXX com
  • uni-app实现多选

  • word2016解决mathtype无法加载mathpage.wll文件问题

    参考文章 https www cnblogs com weiyouqing p 9082353 html 首先需要找到在Word加载的两个文件 一个是MathType Commands 6 For Word2010 dotm 文件位置 C
  • 拥塞控制算法

    TCP拥塞控制算法的目的可以简单概括为 公平竞争 充分利用网络带宽 降低网络延时 优化用户体验 然而就目前而言要实现这些目标就难免有权衡和取舍 算法分类 基于丢包策略的传统拥塞控制算法的几个迭代版本 如图所示 与此同时还有一类算法是基于RT
  • 计算机二级(Python)__第三方库

    Python第三方库依照安装方式灵活性和难易程度有3个方法 建议一次使用 这三个方法是 pip工具安装 自定义安装和文件安装 pip工具安装 最常用且最高效的Python第三方库安装方式采用pip工具安装 pip是Python官方提供并维护
  • spring深入学习(十九) IOC 之 Factory 实例化 bean

    这篇我们关注创建 bean 过程中的第一个步骤 实例化 bean 对应的方法为 createBeanInstance 如下 protected BeanWrapper createBeanInstance String beanName R
  • 服务端和客户端的区别及介绍

    客户端和服务器通常是值互联网硬件所扮演的主要角色 客户端又称为用户端 与服务器相对应 与服务器端相互配合运行 下面是两者的不同点 1 定义不同 客户端 或称为用户端 是指与服务器相对应 为客户提供本地服务的程序 服务器端 从广义上讲 服务器
  • STM32 代码大小的计算与优化

    一 代码大小 1 类别 code 包含两部分 即代码和数据 1 code 即程序代码部分 inline data For example literal pools 文字常量池 and short strings 短字符串 等 这个一般被忽
  • python第三方库 pip install速度慢的解决办法

    文章目录 1 在命令中指定国内镜像网站 2 永久配置源 2 1 linux 系统配置 2 2 windows 系统 1 在命令中指定国内镜像网站 阿里云 https mirrors aliyun com pypi simple 中国科技大学
  • chckBox样式的修改

    在Android开发中 系统自带的默认CheckBox由于比较简陋 可能难以满足部分人的审美需求 不过 Android具有很强的扩展性 自定义CheckBox其实也很简单 1 Layout中定义CheckBox
  • 【护眼色设置】Adobe Acrobat DC / Notepad++ 背景颜色设青苹果绿

    目录 前言 护眼色 Adobe Acrobat DC Notepad 参考 前言 Acrobat DC可将纸质图片 文字迅速转化成PDF或文档格式 比如人们通过手机拍照 可让纸质版文字转化成电子版 用户可直接对文档进行修改 Notepad
  • 记录CSDN一些代码内容不能复制的解决方法

    控制台修改css样式 content views pre css user select text content views pre code css user select text content views pre css user
  • 嵌入式100题(86):简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?画出CPU核、cache、MMU、内存之间的关系示意图加以说明...

    简述处理器在读内存的过程中 CPU核 cache MMU如何协同工作 画出CPU核 cache MMU 内存之间的关系示意图加以说明 现代操作系统普遍采用虚拟内存管理机制 这需要MMU Memory Management Unit 内存管理
  • AIOps 在腾讯的探索和实践

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由LemonLu发表于云 社区专栏 赵建春 腾讯 技术运营通道主席 腾讯 社交网络运营部助理总经理 AIOps 白皮书核心编写专家 我今天要讲的主题 AIOps 是一个比较新的话
  • Qt静态库编译

    下载Qt5 15 2源码库 且已安装emscripten Download Source Package Offline Installers Qt Qt for WebAssembly Qt 5 15 第一步 configure pref
  • 图的深度优先遍历:DFS遍历

    图的深度优先遍历 DFS遍历 提示 系列图的文章 提示 大厂笔试面试都可能不咋考的数据结构 图 由于图的结构比较难 出题的时候 很难把这个图的数据搞通顺 而且搞通顺了题目也需要耗费太多时间 故笔试面试都不会咋考 笔试大厂考的就是你的贪心取巧