搜索算法(四) 广度优先搜素算法

一、BFS

bfs一层一层地遍历图或树,一般用队列实现,可以计算距离目标的步数。

 

二、例题

1)

力扣icon-default.png?t=N4P3https://leetcode.cn/problems/shortest-bridge/ 这道题实际是计算两个岛屿之间的最短距离,可以先用dfs搜索到第一个岛屿并且记录第一个岛屿的每对坐标,接着以这些坐标作为bfs的起始节点集合,一层一层地向外遍历,寻找第二个岛屿,当找到第二个岛屿时,当前遍历的层数减一就是两个岛屿之间的最短路径。

tips: 将第一个岛屿的坐标值标为2,方便区分两个岛屿

       bfs遍历过程中,将0标为2,表示已加入了第一个岛屿

class Solution {
public:int shortestBridge(vector<vector<int>>& grid) {int n = grid.size();for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(grid[i][j]==1){dfs(grid,i,j);return bfs(grid);}}}return 0;}int bfs(vector<vector<int>>& grid){int level = 0;int m;int n = grid.size();while(!first.empty()){level++;m = first.size();while(m--){auto[a,b] = first.front();first.pop();for(int i=0;i<4;i++){int x = a + path[i];int y = b + path[i+1];if(x>=0 && y>=0 && x<n && y<n){if(grid[x][y]==0){grid[x][y] = 2;first.push({x,y});}else if(grid[x][y]==1){return level-1;}}}}}return 0;}void dfs(vector<vector<int>>& grid, int a, int b){grid[a][b] = 2;first.push({a,b});int n = grid.size();for(int i=0;i<4;i++){int x = a + path[i];int y = b + path[i+1];if(x>=0 && y>=0 && x<n && y<n && grid[x][y]==1){dfs(grid, x, y);}}}private:queue<pair<int,int>> first;vector<int> path{-1,0,1,0,-1};
};

2)

力扣icon-default.png?t=N4P3https://leetcode.cn/problems/word-ladder-ii/单词接龙可以理解成寻找两个单词之间的最短路径,当两个单词之间只有一个字母不相同时可以认为两个单词之间有一条双向边,这道题就是寻找起始单词到目标单词的最短路径。

用BFS从起始单词开始,变换一个字母,代表走了一步,变换两个字母,代表走了两步了,一层一层变换,一直遍历到达目标单词。

在一层一层遍历的过程中,需要记录下每一层变换所得到的单词,这样BFS结束后,可以用DFS找到最短路径。

我一开始用的是双向BFS+DFS,基于正向的逻辑,在第34个测试用例时超时了。

class Solution {
public:vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {vector<vector<string>> ans;unordered_set<string> dict;for(auto w:wordList){dict.insert(w);}if(dict.count(endWord)==0){return ans;}dict.erase(beginWord);dict.erase(endWord);unordered_set<string> q1{beginWord}, q2{endWord};unordered_map<string,vector<string>> next;bool reversed = false, found = false;while(!q1.empty()){unordered_set<string> q;for(const auto& w:q1){string s = w;for(int i=0;i<s.size();i++){char ch = s[i];for(int j=0;j<26;j++){s[i] = 'a' + j;if(q2.count(s)){reversed?next[s].push_back(w):next[w].push_back(s);found = true;}if(dict.count(s)){reversed?next[s].push_back(w):next[w].push_back(s);q.insert(s);}}s[i] = ch;}}if(found){break;}for(const auto& w:q){dict.erase(w);}if(q.size()<q2.size()){q1 = q;}else{reversed = !reversed;q1 = q2;q2 = q;}}if(found){vector<string> path;path.push_back(beginWord);dfs(beginWord,endWord,next,path,ans);}return ans;}void dfs(string beginWord, string endWord, unordered_map<string,vector<string>>& next, vector<string>& path, vector<vector<string>>& ans ){if(beginWord==endWord){ans.push_back(path);}for(const auto& w:next[beginWord]){path.push_back(w);dfs(w,endWord,next,path,ans);path.pop_back();}}
};

结果:

 

看了一下大家的讨论,发现错误在于DFS搜索是正向搜索的(从起始单词向目标单词搜索),这样会导致指数级别的可能路径。

为了不超时,正确做法是从目标单词反向向起始单词搜索,这样可能路径会少很多。

将前面的代码简单修改:记录每一层原单词变换所得到的所有单词,改为记录单词可由哪些单词得到。DFS从目标单词开始搜索,反转最终路径。

class Solution {
public:vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {vector<vector<string>> ans;unordered_set<string> dict;for(auto w:wordList){dict.insert(w);}if(dict.count(endWord)==0){return ans;}dict.erase(beginWord);dict.erase(endWord);unordered_set<string> q1{beginWord}, q2{endWord};unordered_map<string,vector<string>> next;bool reversed = false, found = false;while(!q1.empty()){unordered_set<string> q;for(const auto& w:q1){string s = w;for(int i=0;i<s.size();i++){char ch = s[i];for(int j=0;j<26;j++){s[i] = 'a' + j;if(q2.count(s)){reversed?next[w].push_back(s):next[s].push_back(w);found = true;}if(dict.count(s)){reversed?next[w].push_back(s):next[s].push_back(w);q.insert(s);}}s[i] = ch;}}if(found){break;}for(const auto& w:q){dict.erase(w);}if(q.size()<q2.size()){q1 = q;}else{reversed = !reversed;q1 = q2;q2 = q;}}if(found){vector<string> path;path.push_back(endWord);dfs(endWord, beginWord,next,path,ans);}return ans;}void dfs(string beginWord, string endWord, unordered_map<string,vector<string>>& next, vector<string>& path, vector<vector<string>>& ans ){if(beginWord==endWord){reverse(path.begin(), path.end());ans.push_back(path);reverse(path.begin(), path.end());}for(const auto& w:next[beginWord]){path.push_back(w);dfs(w,endWord,next,path,ans);path.pop_back();}}
};

结果:

 这个结果不算好,我就把双向BFS改成单向了,执行时间明显变快了,说明双向BFS属实没必要。

class Solution {
public:vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {vector<vector<string>> ans;unordered_set<string> dict;for(auto w:wordList){dict.insert(w);}if(dict.count(endWord)==0){return ans;}dict.erase(beginWord);unordered_set<string> q1{beginWord};unordered_map<string,vector<string>> next;bool found = false;while(!q1.empty()){unordered_set<string> q;for(const auto& w:q1){string s = w;for(int i=0;i<s.size();i++){char ch = s[i];for(int j=0;j<26;j++){s[i] = 'a' + j;if(s==endWord){ next[s].push_back(w);found = true;break;}if(dict.count(s)){next[s].push_back(w);q.insert(s);}}s[i] = ch;}}if(found){break;}for(const auto& w:q){dict.erase(w);}q1 = q;}if(found){vector<string> path;path.push_back(endWord);dfs(endWord, beginWord,next,path,ans);}return ans;}void dfs(string beginWord, string endWord, unordered_map<string,vector<string>>& next, vector<string>& path, vector<vector<string>>& ans ){if(beginWord==endWord){reverse(path.begin(), path.end());ans.push_back(path);reverse(path.begin(), path.end());}for(const auto& w:next[beginWord]){path.push_back(w);cout<<"*push****        "<<w<<endl;dfs(w,endWord,next,path,ans);cout<<"-pop-----        "<<w<<endl;path.pop_back();}}
};

 

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

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

相关文章

关于万物悦享推广案例

关于万物悦享推广案例 项目介绍 万物悦享是一家改变传统消费模式的公司&#xff0c;致力于让消费者在衣食住行都能把消费变成开心享受的事情。该公司通过消费增值、绿色积分、12倍通证强制出局、卷轴和撸实现这一目标。在通证经济下&#xff0c;消费者可以通过获得通证再赚回…

Python字典及用法详解

Python中的字典&#xff08;Dictionary&#xff09;是一种无序、可变的数据类型&#xff0c;用于存储键&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;之间的映射关系。字典是一种高效的数据结构&#xff0c;可以用于快速查找和检索数据。 1.创建字典 可以使…

人事项目开发记录-登录模块

人事项目开发记录 后端接口实现 后端接口实现 后端权限认证采用Spring Security实现&#xff08;本小节中大量知识点与第10章的内容相关&#xff0c;需要读者熟练掌握第10章的内容&#xff09;&#xff0c;数据库访问使用MyBatis&#xff0c;同时使用Redis实现认证信息缓存。因…

Alertmanager的pod如何添加标签(label)

在Alertmanager.spec.podMetadata字段下添加&#xff0c;如下图&#xff1a;

蘑菇街购物商城

P148-P151 项目创建 项目我用脚手架3创建&#xff1a;vue creat supermall (这个项目名字是supermall)后面配置直接选Babel 运行项目&#xff1a;npm run serve(因为我们观察创建好的项目的初始文件目录&#xff0c;没有config,说明这个使用脚手架3创建的&#xff0c;可以去查…

蘑菇街服务器信息,蘑菇街开放平台

一、授权方式 为保证用户数据的安全性&#xff0c;若您的应用已完成与蘑菇街开放平台对接&#xff0c;需要获取一些与用户紧密相关的信息(如订单、商品、促销等)&#xff0c;需要征得用户的同意&#xff0c;获得用户的授权许可。蘑菇街开放平台采用国际通用的OAuth2.0标准协议&…

仿蘑菇街界面(2)

上一篇博客&#xff0c;博客地址http://blog.csdn.net/itbailei/article/details/38561297把基本的主界面框架已经搭建完毕&#xff0c;我们采用的基本框架为fragment进行页面之间的切换&#xff0c;底部菜单采用的是RadioButton。今天我们来重点来仿照一下第一个底部菜单“爱逛…

仿蘑菇街界面应用(1)

看到郭霖大神仿微信主界面的博客&#xff0c;在佩服大神文笔犀利、讲解详尽、代码风骚之余&#xff0c;也想在上班无所事事时&#xff0c;找点有意思的东西玩玩&#xff0c;蘑菇街作为中国最大女性购物社区&#xff0c;其APP的设计水平也毋庸置疑的&#xff0c;最近博客将连续来…

实现蘑菇街首页效果

打算出一个系列&#xff0c;专治现在市面上各种app的各种滑动不服系列&#xff0c;解决各种滑动冲突问题&#xff0c;现在已经发现了9种样式&#xff0c;打算一个一个一一破解&#xff0c;这是第一篇。 今天给大家带来的是高仿蘑菇街的首页&#xff0c;现在这种页面的格式很流…

设备指纹系列--基础篇

基础概念 618还没开始&#xff0c;但是又好像已经结束了…在这种电商大促的大节日前&#xff0c;电商行业客户一般会提前找到合适的设备指纹产品&#xff0c;去防止被“薅秃”。因为&#xff0c;黑灰产拥有专业的设备牧场&#xff0c;通过使用模拟器、刷机改机等手段&#xff…

仿蘑菇街个人主页

效果图&#xff1a; 看到效果图&#xff0c;第一想到的大致布局是一个scrollview嵌套一个viewpage&#xff0c;viewpage里面有一两个fragment或者写成一个fragment。但是fragment肯定包含两个布局&#xff0c;一个是含有图片(gridview)的listview&#xff0c;另一个布局是只含有…

App竞品分析报告:美丽说VS蘑菇街

1.产品概况 iOS App Store中国区iPhone免费-生活类排名&#xff08;最近3个月&#xff09; 数据来源&#xff1a;ann9.com 蘑菇街排名基本稳定在Top 10至20之间&#xff0c;美丽说在8月下旬后基本游离在Top 30外。 2015年6月活跃用户数比对-iOS端 数据说明&#xff1a;MAU为月…

社会化购物:Pinterest,Fancy还是美丽说,蘑菇街?

转自&#xff1a;网站分析在中国 原文地址&#xff1a;http://www.chinawebanalytics.cn/social-shopping-pinterest-or-fancy/ 【每期一句】越强烈的网络效应&#xff0c;越接近成功。 【前言】这篇文章是应 的邀请所做。很高兴能有机会与几年前一样&#xff0c;分析一个细分行…

仿蘑菇街项目

引言 仿蘑菇街的Vue.js项目是我学习vue.js做的第一个项目&#xff0c;今天来重温一下项目实现的功能&#xff0c;记录一下&#xff0c;方便以后查看。首先需要创建项目&#xff0c;本项目采用cli-3脚手架创建项目&#xff0c;采用默认安装模式&#xff0c;没有安装vue-router和…

高仿蘑菇街欢迎页

####蘑菇街欢迎页 ####高仿效果 这里这里…Demo下载地址 #####前言 本文将介绍如何对蘑菇街欢迎页效果进行分析&#xff0c;拆分&#xff0c;并一步步实现1个高仿版本&#xff0c;最重要的设计思路包括以下2点&#xff1a; 1.ViewPager切换时&#xff0c;通过offset偏移量动…

美丽说蘑菇街首页效果(UITableView和UIScrollerView联动)

作为一名菜鸟iOS开发程序员&#xff0c;第一次写文章&#xff0c;有点小激动&#xff01;进入正题&#xff0c;最近项目中有个需求&#xff0c;类似美丽说蘑菇街首页效果&#xff0c;在网上找了一些资料后自己研究了下终于搞定了&#xff01; 先看效果&#xff1a; 接下来详细…

【Linux】Nginx 优化与防盗链

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Nginx 优化与防盗链 一、隐藏版本号方法一&#xff1a;修改配置文件方式方法二&#xff1a;修改源码文件&#xff0c;重新编译安装 二、修改用户与组三、缓存时间四、日志切割…

操作系统的最强入门科普(Unix/Linux篇)

今天这篇文章&#xff0c;我们来聊聊操作系统&#xff08;Operating System&#xff09;。 说到操作系统&#xff0c;大家都不会陌生。我们天天都在接触操作系统——用台式机或笔记本电脑&#xff0c;使用的是windows和macOS系统&#xff1b;用手机、平板电脑&#xff0c;则是a…

PDF文件无法编辑怎么办

PDF文件无法编辑是因为设置了编辑限制&#xff0c;只要在设置密码的地方输入密码把密码取消就可以自由编辑文件了。如果不知道密码或者忘记了密码&#xff0c;只能使用第三方的解密软件把密码解除掉&#xff0c;现在有很多PDF的辅助软件&#xff0c;可以在网上搜到很多&#xf…

SpringBoot实现服务器PDF文件的下载和预览功能

&#x1f345;程序员小王的博客&#xff1a;程序员小王的博客 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 如有编辑错误联系作者&#xff0c;如果有比较好的文章欢迎分享给我&#xff0c;我会取其精华去其糟粕 &#x1f345;java自学的学习…