BFS(广度优先搜索)——搜索算法

news2025/2/6 8:49:20

        BFS,也就是广度(宽度)优先搜索,二叉树的层序遍历就是一个BFS的过程。而前、中、后序遍历则是DFS(深度优先搜索)。从字面意思也很好理解,DFS就是一条路走到黑,BFS则是一层一层地展开。

关于二叉树的创建与遍历,大家可以参考下文:

二叉树创建和遍历_二叉树的建立与遍历-CSDN博客

下图分别是DFS和BFS的遍历顺序图解: 

    

一、层序遍历

接下来我们直接来看题,从题中感受BFS的魅力。

        N叉树与二叉树的区别无非就是二叉树最多只有两个子节点用left和right两个指针就够储存。而N叉树有N多个节点,需要一个指针数组来储存

在用BFS解决问题时几乎都要借助队列结构,用队列的先进先出特性来储存上一层的数据。

        首先把根节点放到队列里面,然后从队列里面取节点出来,把节点取出来后,通过它找到它的子节点并带入到队列里面。那么这个节点的使命就算完成了,需要把它踢出队列。循环往复直到最后队列为空,则完成层序遍历。

        但对于这个题并不是简单的层序遍历就行,它还要把每一层划分出来。这个其实也很简单,只要记录每一层有几个节点(记为n个),然后一次性的遍历n个,并把这n个元素用数组储存起来。可以直接使用size()计算该层元素个数。

代码示例:

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root)
    {
        if(!root) return {};
        vector<vector<int>> ret;
        queue<Node*> qu;
        qu.push(root);
        while(!qu.empty())
        {
            vector<int> arr;
            int n=qu.size();//获取该层有一个元素
            while(n--)//取出该层的所以元素并把子节点带入
            {
                Node* rt=qu.front();
                qu.pop();
                arr.push_back(rt->val);
                for(int i=0;i<rt->children.size();i++)
                    qu.push(rt->children[i]);
            }
            if(!arr.empty()) ret.push_back(arr);
        }    
        return ret;
    }
};

二、最短路问题

例如我们要找到从起点(1)到终点(8)的最短路径。

最终找到最短路如下: 

例题:

首先因为它是上下左右式的扩散,所以使用一个小技巧:向量坐标

dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};

我们使用

for(int k=0;k<4;k++)

        x1 = x+dx[k], y1 = y+dy[k];

就可以得到(x, y)的上下左右坐标的(x1, y1)。

        它的扩展逻辑就和上文层序遍历中那一题差不多,但需要一个变量记录层数。在扩展过程中需要判断是否是目标值,还需要把扩展到的元素做标记

代码示例:

class Solution {
public:
    int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
    int nearestExit(vector<vector<char>>& maze, vector<int>& entrance)
    {
        int n = maze.size(),m = maze[0].size();
        vector<vector<bool>> hash(n,vector<bool>(m));//记录是否已经搜索过
        queue<pair<int,int>> qu;
        qu.push({entrance[0],entrance[1]});
        hash[entrance[0]][entrance[1]] = true;
        int ret = 0;//记录层数
        while(!qu.empty())
        {
            ret++;
            int sz = qu.size();
            while(sz--)
            {
                auto [a,b] = qu.front();
                qu.pop();
                for(int k=0;k<4;k++)
                {
                    int x = a+dx[k],y = b+dy[k];
                    if(x>=0&&x<n&&y>=0&&y<m&&!hash[x][y]&&maze[x][y]=='.')
                    {
                        if(x==0||x==n-1||y==0||y==m-1) return ret;
                        hash[x][y] = true;
                        qu.push({x,y});
                    }
                }
            }
        }
        return -1;
    }
};

        这个题我们可以做一个多趟的BFS,首先确定第一个起点和终点,进行BFS搜索并记录路径长度。

        注意:由题可知没有两棵树的高度是相同的,也就是说树的高度和它的坐标是一一对应的

具体操作如下:

        把所有树的高度(>1的元素)都记录到一个数组中,然后对该数组进行排序。这样我们就知道搜索的先后顺序。

        封装一个bfs函数,如pair<int,int> bfs(int x,int y,int target,vector<vector<int>>& forest),传入起点坐标与终点的值,它的作用是记录起点到终点的最短距离,然后返回终点的坐标。如果不能被搜索到返回{-1, -1}。

        距离的计算我们可以使用一个全局变量在bfs中累加。

        进行一次bfs后得到的返回值作为下一次搜索的起点下标,而终点值从刚才排序好的数组中取到。

        最后,使用循环把数组中所有的值都搜索一遍。

class Solution {
public:
    int ret = 0,m = 0,n = 0;
    int dx[4] = {0,0,-1,1},dy[4]={-1,1,0,0};
    int cutOffTree(vector<vector<int>>& forest)
    {
        m = forest.size(),n = forest[0].size();
        vector<int> arr;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(forest[i][j]>1) arr.push_back(forest[i][j]);
        sort(arr.begin(),arr.end());
        int x = 0,y = 0;
        for(int i=0;i<arr.size();i++)
        {
            if(i==0&&arr[i]==forest[0][0]) continue;
            auto [a,b] = bfs(x,y,arr[i],forest);
            if(a==-1) return -1;
            x = a,y = b;
        }
        return ret;
    }
    pair<int,int> bfs(int x,int y,int target,vector<vector<int>>& forest)
    {
        vector<vector<bool>> hash(m,vector<bool>(n));
        queue<pair<int,int>> qu;
        qu.push({x,y});
        hash[x][y] = true;
        while(!qu.empty())
        {
            ret++;
            int sz = qu.size();
            while(sz--)
            {
                auto [a,b] = qu.front();
                qu.pop();
                for(int k=0;k<4;k++)
                {
                    int x1 = a+dx[k],y1 = b+dy[k];
                    if(x1>=0&&x1<m&&y1>=0&&y1<n&&!hash[x1][y1]&&forest[x1][y1]!=0)
                    {
                        if(forest[x1][y1]==target) return {x1,y1};
                        hash[x1][y1]=true;
                        qu.push({x1,y1});
                    }
                }
            }
        }
        return {-1,-1};
    }
};

三、多源最短路问题

多源最短路它的特点就是可以选择多个起点,找出到达终点的最短路径。

        其实它和单源最短路差别并不是很大,我们只需要把所有起点看作一个整体,也就是把它当作“超级源点”那么它就是一个单源最短路问题。

        这个题很明显我们可以使用BFS解决,但是如果每个1的位置都用一次BFS搜索,未免也太麻烦,那么我们可以用多源最短路的思想,把所有0当作起点,然后去搜索1并填充最短路径。                那么为什么不用所有的1为起点去搜索0呢,因为我们需要填写的信息是1的位置,如果用1去搜索0的话,最后就不能找到原来那个1的位置。 

代码示例:

class Solution {
public:
    int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat)
    {
        int m=mat.size(),n=mat[0].size();
        vector<vector<int>> ret(m,vector<int>(n,-1));//充当了哈希表的功能
        queue<pair<int,int>> q;
        for(int i=0;i<m;i++)//把所有起点加入队列中
        {
            for(int j=0;j<n;j++)
            {
                if(mat[i][j]==0)
                {
                    q.push({i,j});
                    ret[i][j]=0;
                }
            }
        }
        int tmp = 0;
        while(!q.empty())
        {
            tmp++;
            int sz = q.size();
            while(sz--)
            {
                auto [i,j]=q.front();
                q.pop();
                for(int k=0;k<4;k++)
                {
                    int x=dx[k]+i,y=dy[k]+j;
                    if(x>=0&&x<m&&y>=0&&y<n&&ret[x][y]==-1)
                    {
                        ret[x][y]=tmp;
                        q.push({x,y});
                    }
                }
            }
        }
        return ret;
    }
};

四、拓扑排序问题

在一个有向无环图中,要保证在 “被指向的节点” 出现之前,它的 “父节点” 要先出现。

通常用顶点来表示一个活动,用边的指向来表示活动之间的先后顺序。如:

如上,拓扑排序的结果可能有多种,只要合法就行。

排序方法:

  • 1.记录每个顶点的入度边个数,和出度边的指向。
  • 2.把所有入度边为0的节顶点push到队列中。
  • 3.拿出队头元素加入到结果中。
  • 4.把该元素指向的所有顶点入度边减1,并判断如果有顶点的入度边为0则push到队列。
  • 循环执行3,4部直到队列为空,则完成排序。

210. 课程表 II - 力扣(LeetCode)

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> in(numCourses), ret;
        vector<vector<int>> out(numCourses);
        for(int i=0;i<prerequisites.size();i++)
        {
            int a=prerequisites[i][0];
            int b=prerequisites[i][1];
            in[a]++;
            out[b].push_back(a);
        }    
        queue<int> q;
        for(int i=0;i<numCourses;i++)
            if(in[i]==0) q.push(i);
        while(!q.empty())
        {
            int v = q.front();
            ret.push_back(v);
            q.pop();
            for(int i=0;i<out[v].size();i++)
                if(--in[out[v][i]]==0) q.push(out[v][i]);
        }
        if(ret.size()!=numCourses) return {};
        return ret;
    }
};

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

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

相关文章

33.Word:国家中长期人才发展规划纲要【33】

目录 NO1.2样式​ NO3​ 图表 ​ NO4.5.6​ 开始→段落标记视图→导航窗格→检查有无遗漏 NO1.2样式 F12/另存为&#xff1a;Word.docx&#xff1a;考生文件夹样式的复制样式的修改 样式的应用&#xff08;没有相似/超级多的情况下&#xff09;——替换 [ ]通配符&#x…

gym-anytrading

参考&#xff1a;https://github.com/upb-lea/gym-electric-motor AnyTrading 是一组基于 reinforcement learning (RL) 的 trading algorithms&#xff08;交易算法&#xff09;的 OpenAI Gym 环境集合。 该项目主要用于foreign exchange (FOREX) 和 stock markets (股票市场)…

如何自定义软件安装路径及Scoop包管理器使用全攻略

如何自定义软件安装路径及Scoop包管理器使用全攻略 一、为什么无法通过WingetUI自定义安装路径&#xff1f; 问题背景&#xff1a; WingetUI是Windows包管理器Winget的图形化工具&#xff0c;但无法直接修改软件的默认安装路径。原因如下&#xff1a; Winget设计限制&#xf…

私有化部署 DeepSeek + Dify,构建你的专属私人 AI 助手

私有化部署 DeepSeek Dify&#xff0c;构建你的专属私人 AI 助手 概述 DeepSeek 是一款开创性的开源大语言模型&#xff0c;凭借其先进的算法架构和反思链能力&#xff0c;为 AI 对话交互带来了革新性的体验。通过私有化部署&#xff0c;你可以充分掌控数据安全和使用安全。…

Java 进阶 01 —— 5 分钟回顾一下 Java 基础知识

Java 进阶 01 —— 5 分钟回顾一下 Java 基础知识 Java 生态圈Java 跨平台的语言 Java 虚拟机规范JVM 跨语言的平台多语言混合编程两种架构 举例 JVM 的生命周期 虚拟机的启动虚拟机的执行虚拟机的退出 JVM 发展历程 Sun Classic VMExact VMHotSpotBEA 的 JRockitIBM 的 J9 …

V103开发笔记1-20250113

2025-01-13 一、应用方向分析 应用项目&#xff1a; PCBFLY无人机项目&#xff08;包括飞控和手持遥控器&#xff09;&#xff1b; 分析移植项目&#xff0c;应用外设资源包括&#xff1a; GPIO, PWM,USART,GPIO模拟I2C/SPI, ADC,DMA,USB等&#xff1b; 二、移植项目的基本…

DeepSeek研究员在线爆料:R1训练仅用两到三周,春节期间观察到R1 zero强大进化

内容提要 刚刚我注意到DeepSeek研究员Daya Guo回复了网友有关DeepSeek R1的一些问题&#xff0c;以及接下来的公司的计划&#xff0c;只能说DeepSeek的R1仅仅只是开始&#xff0c;内部研究还在快速推进&#xff0c;DeepSeek 的研究员过年都没歇&#xff0c;一直在爆肝推进研究…

LLM推理--vLLM解读

主要参考&#xff1a; vLLM核心技术PagedAttention原理 总结一下 vLLM 的要点&#xff1a; Transformer decoder 结构推理时需要一个token一个token生成&#xff0c;且每个token需要跟前序所有内容做注意力计算&#xff08;包括输入的prompt和该token之前生成的token&#xf…

vscode软件操作界面UI布局@各个功能区域划分及其名称称呼

文章目录 abstract检查用户界面的主要区域官方文档关于UI的介绍 abstract 检查 Visual Studio Code 用户界面 - Training | Microsoft Learn 本质上&#xff0c;Visual Studio Code 是一个代码编辑器&#xff0c;其用户界面和布局与许多其他代码编辑器相似。 界面左侧是用于访…

一键开启/关闭deepseek

一键开启/关闭 Deepseek对应下载的模型一键开启 Deepseek&#xff0c;一键关闭Deepseek双击对应的bat&#xff0c;就可以启动https://mbd.pub/o/bread/Z56YmpZvbat 下载&#xff1a;https://mbd.pub/o/bread/Z56YmpZv 可以自己写下来&#xff0c;保存成bat文件&#xff0c;也可…

单纯接入第三方模型就无需算法备案了么?

随着人工智能技术的快速发展&#xff0c;越来越多的企业开始接入第三方模型以提升自身业务能力。然而&#xff0c;关于算法备案的问题也引发了诸多讨论&#xff0c;尤其是单纯接入第三方模型是否需要备案这一问题&#xff0c;更是让不少企业感到困惑。 一、明确算法备案的主体…

DS图(中)(19)

文章目录 前言一、图的遍历广度优先遍历深度优先遍历 二、最小生成树Kruskal算法Prim算法两种方法对比 总结 前言 承上启下&#xff0c;我们来学习下图的中篇&#xff01;&#xff01;&#xff01; 一、图的遍历 图的遍历指的是遍历图中的顶点&#xff0c;主要有 广度优先遍历 …

YK人工智能(六)——万字长文学会基于Torch模型网络可视化

1. 可视化网络结构 随着深度神经网络做的的发展&#xff0c;网络的结构越来越复杂&#xff0c;我们也很难确定每一层的输入结构&#xff0c;输出结构以及参数等信息&#xff0c;这样导致我们很难在短时间内完成debug。因此掌握一个可以用来可视化网络结构的工具是十分有必要的…

Gitea+Gridea 创建个人博客

历史文档存档&#xff0c;该方法目前已经无法使用&#xff0c;部署方法可供参考 Gitea部分 1.关于Gitea Gitea 是一个面向开源及私有软件项目的托管平台&#xff0c;是全球最大的代码托管平台之一。它采用 Git 分布式版本控制系统&#xff0c;为开发者提供了代码托管、版本控…

【Linux】一文带你入门了解线程和虚拟地址空间中页表映射的秘密(内附手绘底层逻辑图 通俗易懂)

绪论​ 每日激励&#xff1a;“努力去做自己该做的&#xff0c;但是不要期待回报&#xff0c;不是付出了就会有回报的&#xff0c;做了就不要后悔&#xff0c;不做才后悔。—Jack” 绪论​&#xff1a; 本章是LInux中非常重要的线程部分&#xff0c;通过了解线程的基本概念&am…

Flash Attention与Attention

原始Attention是&#xff1a; Flash Attention&#xff1a; 伪代码&#xff1a;4d&#xff08;分别代表Q\K\V\O&#xff09; Flash Attention2优化了

JAVA进阶之线程

为神马有线程&#xff1f;这玩意儿在干嘛&#xff1f;&#xff1f;&#xff1f; 回答这个问题&#xff0c;就先要知道一点点计算机的工作方式。 总所周知&#xff0c;计算机有五部分&#xff1a;输入输出、计算器、存储器、控制器。而在计算机内&#xff0c;CPU、内存、I/O之…

机器学习专业毕设选题推荐合集 人工智能

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

C++ 中的 `string` 类型:全面解析与高效操作

C 中的 string 类型&#xff1a;全面解析与高效操作 在 C 中&#xff0c;string 类型是对字符数组的高级封装&#xff0c;它提供了大量内置函数&#xff0c;使得字符串的处理变得更为简便和高效。与 C 风格的字符数组不同&#xff0c;string 类型不仅自动管理内存&#xff0c;…

Java入门进阶

文章目录 1、常用API 1.1、Math1.2、System1.3、Object1.4、Arrays1.5、基本类型包装类 1.5.1、基本类型包装类概述1.5.2、Integer1.5.3、int和String相互转换1.5.4、自动装箱和拆箱 1.6、日期类 1.6.1、Date类1.6.2、SimpleDateFormat类 1.6.2.1、格式化&#xff08;从Date到…