算法---DFS和BFS

news2024/11/18 5:47:03

一 : 什么是DFS和BFS?

转载自 : 什么是DFS和BFS?

简介: 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,也频繁出现在高频面试题中。

1.1 DFS

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

树是图的一种特例(连通无环的图就是树),接下来我们来看看树用深度优先遍历该怎么遍历。

在这里插入图片描述
1、我们从根节点 1 开始遍历,它相邻的节点有 2,3,4,先遍历节点 2,再遍历 2 的子节点 5,然后再遍历 5 的子节点 9。

在这里插入图片描述
2、上图中一条路已经走到底了(9是叶子节点,再无可遍历的节点),此时就从 9 回退到上一个节点 5,看下节点 5 是否还有除 9 以外的节点,没有继续回退到 2,2 也没有除 5 以外的节点,回退到 1,1 有除 2 以外的节点 3,所以从节点 3 开始进行深度优先遍历,如下 :

在这里插入图片描述
3、同理从 10 开始往上回溯到 6, 6 没有除 10 以外的子节点,再往上回溯,发现 3 有除 6 以外的子点 7,所以此时会遍历 7 :

在这里插入图片描述
3、从 7 往上回溯到 3, 1,发现 1 还有节点 4 未遍历,所以此时沿着 4, 8 进行遍历,这样就遍历完成了

完整的节点的遍历顺序如下(节点上的的蓝色数字代表):

在这里插入图片描述
相信大家看到以上的遍历不难发现这就是树的前序遍历,实际上不管是前序遍历,还是中序遍历,亦或是后序遍历,都属于深度优先遍历。

那么深度优先遍历该怎么实现呢,有递归和非递归两种表现形式,接下来我们以二叉树为例来看下如何分别用递归和非递归来实现深度优先遍历。

1、递归实现

递归实现比较简单,由于是前序遍历,所以我们依次遍历当前节点,左节点,右节点即可,对于左右节点来说,依次遍历它们的左右节点即可,依此不断递归下去,直到叶节点(递归终止条件),代码如下 :

public class Solution {
    private static class Node {
        /**
         * 节点值
         */
        public int value;
        /**
         * 左节点
         */
        public Node left;
        /**
         * 右节点
         */
        public Node right;

        public Node(int value, Node left, Node right) {
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    public static void dfs(Node treeNode) {
        if (treeNode == null) {
            return;
        }
        // 遍历节点
        process(treeNode)
        // 遍历左节点
        dfs(treeNode.left);
        // 遍历右节点
        dfs(treeNode.right);
    }
}

递归的表达性很好,也很容易理解,不过如果层级过深,很容易导致栈溢出。所以我们重点看下非递归实现 .

2、非递归实现

仔细观察深度优先遍历的特点,对二叉树来说,由于是先序遍历(先遍历当前节点,再遍历左节点,再遍历右节点),所以我们有如下思路 :

1、对于每个节点来说,先遍历当前节点,然后把右节点压栈,再压左节点(这样弹栈的时候会先拿到左节点遍历,符合深度优先遍历要求)
2、弹栈,拿到栈顶的节点,如果节点不为空,重复步骤 1, 如果为空,结束遍历。

我们以以下二叉树为例来看下如何用栈来实现 DFS。

在这里插入图片描述
整体思路还是比较清晰的,使用栈来将要遍历的节点压栈,然后出栈后检查此节点是否还有未遍历的节点,有的话压栈,没有的话不断回溯(出栈),有了思路,不难写出如下用栈实现的二叉树的深度优先遍历代码 :

/**
 * 使用栈来实现 dfs
 * @param root
 */
public static void dfsWithStack(Node root) {
    if (root == null) {
        return;
    }

    Stack<Node> stack = new Stack<>();
    // 先把根节点压栈
    stack.push(root);
    while (!stack.isEmpty()) {
        Node treeNode = stack.pop();
        // 遍历节点
        process(treeNode)

        // 先压右节点
        if (treeNode.right != null) {
            stack.push(treeNode.right);
        }

        // 再压左节点
        if (treeNode.left != null) {
            stack.push(treeNode.left);
        }
    }
}

可以看到用栈实现深度优先遍历其实代码也不复杂,而且也不用担心递归那样层级过深导致的栈溢出问题。

1.2 BFS

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

上文所述树的广度优先遍历示意图如下,每个节点的值即为它们的遍历顺序。所以广度优先遍历也叫层序遍历,先遍历第一层(节点 1),再遍历第二层(节点 2,3,4),第三层(5,6,7,8),第四层(9,10)。

在这里插入图片描述
深度优先遍历用的是栈,而广度优先遍历要用队列来实现 , 具体代码如下 :

/**
 * 使用队列实现 bfs
 * @param root
 */
private static void bfs(Node root) {
    if (root == null) {
        return;
    }
    Queue<Node> stack = new LinkedList<>();
    stack.add(root);

    while (!stack.isEmpty()) {
        Node node = stack.poll();
        System.out.println("value = " + node.value);
        Node left = node.left;
        if (left != null) {
            stack.add(left);
        }
        Node right = node.right;
        if (right != null) {
            stack.add(right);
        }
    }
}

下面是一些练习题目 .

二 : 红与黑

1.题目

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的(上下左右四个方向)黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
在这里插入图片描述

2. 题解

2.1 DFS

在这里插入图片描述

我们可以使用一个标记数组 , 每当遍历过该黑色瓷砖 , 就将标记置为true .

具体代码如下 :

// write your code here
//DFS
import java.util.*;

public class Main{
    
    public static int count = 0;
    
    static int[][] direct = {{-1, 0},{1, 0},{0,-1},{0,1}};
    

    private static void dfs(char[][] map,int m,int n,int x,int y,boolean[][] flags) {
       if(flags[x][y]){
           return;
       }
        if('#' == map[x][y]) {
            return;
        }
        
        count++;
        flags[x][y] = true;
        
        for(int i=0; i<4; i++) {
            int newx = x+direct[i][0];
            int newy = y+direct[i][1];
            if(newx >= 0 && newx < m && newy >= 0 && newy < n) {
                dfs(map,m,n,newx,newy,flags);                
            }
        }
        
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        while(sc.hasNext()){
            int m = sc.nextInt();
            int n = sc.nextInt();
            char[][] map = new char[m][n];
            boolean[][] flags =new boolean[m][n];            
            int x = 0;
            int y = 0;
            for(int i=0; i<m; i++) {
                String s = sc.next();
                for(int j=0; j<n; j++) {
                    map[i][j] = s.charAt(j);
                    if('@' == map[i][j]) {
                        x = i;
                        y = j;
                    }
                }
            }
            //map中存储了瓷砖的所有信息
            dfs(map,m,n,x,y,flags);
            System.out.println(count);
            count = 0;
        }
    }
}

进一步思考 , 每当遍历到黑色瓷砖 , 并将其"记录在册"后 , 就将该黑色瓷砖对应的位置标记为白色瓷砖 , 则下一次就不会遍历到这块瓷砖了 . 改进后的代码如下 :

// write your code here
//DFS
import java.util.*;

public class Main{
    
    public static int count = 0;
    
    static int[][] direct = {{-1, 0},{1, 0},{0,-1},{0,1}};

    private static void dfs(char[][] map,int m,int n,int x,int y) {
        if('#' == map[x][y]) {
            return;
        }
        
        count++;
        map[x][y] = '#';
        
        for(int i=0; i<4; i++) {
            int newx = x+direct[i][0];
            int newy = y+direct[i][1];
            if(newx >= 0 && newx < m && newy >= 0 && newy < n) {
                dfs(map,m,n,newx,newy);                
            }
        }
        
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        while(sc.hasNext()){
            int m = sc.nextInt();
            int n = sc.nextInt();
            char[][] map = new char[m][n];
            int x = 0;
            int y = 0;
            for(int i=0; i<m; i++) {
                String s = sc.next();
                for(int j=0; j<n; j++) {
                    map[i][j] = s.charAt(j);
                    if('@' == map[i][j]) {
                        x = i;
                        y = j;
                    }
                }
            }
            //map中存储了瓷砖的所有信息
            dfs(map,m,n,x,y);
            System.out.println(count);
            count = 0;
        }
    }
}

2.1 BFS

借助队列实现广度优先搜素 .

// write your code here
//DFS
import java.util.*;

public class Main{
    static class Node{
        int x, y;
        public Node(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    
    public static int count = 0;
    
    static int[][] direct = {{-1, 0},{1, 0},{0,-1},{0,1}};
    

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        while(sc.hasNext()){
            int m = sc.nextInt();
            int n = sc.nextInt();
            char[][] map = new char[m][n];
            boolean[][] flags =new boolean[m][n];            
            int x = 0;
            int y = 0;
            Node cur = null;
            for(int i=0; i<m; i++) {
                String s = sc.next();
                for(int j=0; j<n; j++) {
                    map[i][j] = s.charAt(j);
                    if('@' == map[i][j]) {
                        count++;
                        cur = new Node(i,j);
                    }
                    if('#' == map[i][j]) {
                        flags[i][j] = true;
                    }
                }
            }
            //map中存储了瓷砖的所有信息
            Queue<Node> queue = new LinkedList<>();
            queue.offer(cur);
            flags[cur.x][cur.y] = true;
            while(!queue.isEmpty()) {
                Node node = queue.poll();
                //搜索四个方向
                for(int i=0; i<4; i++) {
                    Node next = new Node(node.x+direct[i][0],node.y+direct[i][1]);
                    if(next.x >= 0 && next.x < m && 
                       next.y >= 0 && next.y < n &&
                      !flags[next.x][next.y]) {
                        count++;
                        queue.offer(next);
                        flags[next.x][next.y] = true;
                    }
                }
            }
            System.out.println(count);
            count = 0;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82618.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考高级信息系统项目管理师如何备考?

从以下两个方面&#xff1a; 1.首先分析一下高项考试的各个科目&#xff1b; 2.如何备考高项&#xff1f; 高项考试有三个科目&#xff1a; 综合知识&#xff0c;案例分析&#xff0c;和论文。 一、综合知识 信息系统项目管理师上午综合知识科目范围广&#xff0c;知识点非…

【差分进化算法】基于适应度-距离-平衡的自适应引导差分进化 (FDB-AGDE) 算法附matlab代码

​✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法…

java工作流开源框架可以提高工作效率吗?

要想回答这个问题&#xff0c;就需要了解什么是java工作流开源框架&#xff0c;以及java工作流开源框架的主要特点是什么。随着大数据时代的拓展发展&#xff0c;低代码开发平台已经在数字化管理时代中深受欢迎&#xff0c;是做好数据管理和提升企业数字化发展步伐的重要工具。…

医疗机构 IT 管理员保护患者数据和隐私的 3 项必做之事

自疫情开始以来&#xff0c;医疗机构的信息存储与管理正面临着巨大的考验。患者的健康史&#xff0c;包括所有治疗、程序、处方、实验室测试和扫描报告&#xff0c;都以电子健康记录 (EHR) 的形式存储。尽管 EHR 更能提高患者病例的准确性&#xff0c;并帮助医生跟踪患者的医疗…

网络三层交换机部署实验

♥️作者&#xff1a;小刘在C站 ♥️每天分享云计算网络运维课堂笔记&#xff0c;疫情之下&#xff0c;你我素未谋面&#xff0c;但你一定要平平安安&#xff0c;一 起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放&#xff0c;愿…

排查cpu飚高问题-两种方案

cpu 飚高原因 频繁创建线程 线程内进行频繁计算 模拟代码 SpringBootApplicationEnableSchedulingpublic class CrawlBigDataApplication {public static void main(String[] args) { /*19*/ SpringApplication.run(CrawlBigDataApplication.class, (String[])args)…

【并发编程】Atomic类

一、介绍 在java.util.concurrent.atomic包下atomic一般指原子操作类&#xff0c;主要分为四种类型的原子更新类&#xff1a;原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。 二、简单使用 1.AtomicInteger 通过synchronized关键字来保证原子性&#xf…

社交平台数据提取:Social Phone Extractor

Social Phone Extractor是一个功能强大且创新的程序&#xff0c;能够搜索和扫描在 Google / Bing / Yahoo 中索引并与最重要的社交网络&#xff08;如 Linkedin、Facebook、Twitter 和 Instagram&#xff09;相关的个人资料、帖子和文章的页面&#xff0c;然后捕获和推断&#…

ChatGPT介绍世界杯历史与编写足球游戏python程序

ChatGPT聊天机器人最近非常流行&#xff0c;是由OpenAI于本月发布的。花了一点时间注册了一个账号&#xff0c;如有需要帮助注册的可以随时与我交流。注册过程相对有一些复杂。 除了常规的聊天对话功能之外&#xff0c;ChatGPT聊天机器具备强大的文本生成能力&#xff0c;例如博…

【Android】Broadcast广播的使用

一、广播机制概述 通常情况下在学校的每个教室都会装有一个喇叭&#xff0c;这些喇叭是接入到学校广播室的。如果有重要通知&#xff0c;会发送一条广播来告知全校师生。为了便于发送和接收系统级别的消息通知&#xff0c;Android系统也引入了一套类似广播的消息机制。 Android…

股票撤单委托接口是如何操作的?

在交易的过程中&#xff0c;要先通过股票撤单委托接口获取委托单列表的数据&#xff0c;才可以进行撤单的操作&#xff0c;部分的数据会作为参数传递给撤单函数&#xff0c;下面来具体看看股票撤单委托接口是如何操作的&#xff1f; std::cout << " 撤单委托 \n&qu…

025_SSS_BeLFusion: Latent Diffusion for Behavior-Driven Human Motion Prediction

BeLFusion: Latent Diffusion for Behavior-Driven Human Motion Prediction 本文关注的问题是human motion prediction&#xff08;HMP&#xff09;&#xff0c;也就是在给定观测到的人体运动的前提下&#xff0c;预测人体的后续运动。本文的思路是&#xff0c;将人的behavio…

Linux文件服务NFS共享存储服务

作者简介&#xff1a;一名99年软件运维应届毕业生&#xff0c;正在自学云计算课程。宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。创作不易&#xff0c;动动小手…

高视医疗在港交所上市:IPO首日跌破发行价,高铁塔为控股股东

12月12日&#xff0c;高视医疗&#xff08;HK:02407&#xff09;在港交所上市。本次上市&#xff0c;高视医疗的发行价确定为每股发售股份51.40港元。据此计算&#xff0c;高视医疗预计募资约6.72亿港元。而招股书则显示&#xff0c;该公司预计募资净额约为2.83亿港元&#xff…

java 剑指 Offer 57 - II. 和为s的连续正数序列

题目所属分类 双指针的做法 O&#xff08;n)的时间复杂度 同时末尾有List<int[]> res 这种的转化成二位数组 原题链接 输入一个正整数 target &#xff0c;输出所有和为 target 的连续正整数序列&#xff08;至少含有两个数&#xff09;。 序列内的数字由小到大排列&…

Spring Boot自定义starters

一、简介 SpringBoot 最强大的功能就是把我们常用的场景抽取成了一个个starter&#xff08;场景 启动器&#xff09;&#xff0c;我们通过引入springboot 为我提供的这些场景启动器&#xff0c;我们再进行 少量的配置就能使用相应的功能。即使是这样&#xff0c;springboot也不…

R语言绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)

上期咱们已经介绍了咱们绘制复杂抽样设计数据的基础图形&#xff0c;今天咱们来介绍一下咱们绘制复杂抽样设计cox回归生存曲线(Kaplan-Meier)。 废话不多说咱们先导入数据和R包 library(survey) pbc<-read.csv("E:/r/test/pbc.csv",sep,,headerTRUE) 这是一个原…

web前端-javascript-prototype原型(说明,访问,优势,判断是否含有一个属性,原型对象的原型)

prototype 原型 1. 原型介绍 function Person() {} function MyClass() {}//向MyClass中添加属性a MyClass.prototype.a 123;//向MyClass的原型中添加一个方法 MyClass.prototype.sayHello function () {alert("hello"); };var mc new MyClass(); var mc2 new …

使用VUE自定义组件封装数据字典实战

背景 照惯例&#xff0c;先交待下背景&#xff0c;从真实需求出发&#xff0c;讲述设计思路和实现方式。 软件系统中&#xff0c;会有一些成组的常量值&#xff0c;来描述业务实体的属性&#xff0c;如性别、证件类型、审批状态等。我们通常称之为数据字典&#xff0c;作为系统…

Seata

Seata的三大角色 TC&#xff08;Transaction Coordinator&#xff09;-事务协调者 维护全局和分支事务的状态&#xff0c;驱动全局事务提交或回滚。 TM&#xff08;Transaction Manager&#xff09;-事务管理器 定义全局事务的范围&#xff1a;开始全局事务、提交或回滚全局…