算法基础学习笔记——⑩DFS与BFS\树与图

news2024/11/28 21:40:42

✨博主:命运之光
✨专栏:算法基础学习

在这里插入图片描述

目录

DFS与BFS\树与图

✨DFS

✨BFS

🍓宽搜流程图如下:

🍓宽搜流程:

🍓广搜模板

✨树与图

🍓树是特殊的图(连通无环的图)

🍓树与图的存储:

🍓用宽搜框架来搜索图:


前言:算法学习笔记记录日常分享,需要的看哈O(∩_∩)O,感谢大家的支持!


 DFS与BFS\树与图

✨DFS

//回溯,剪枝

当使用深度优先搜索(DFS)回溯算法来搜索图时,我们需要考虑以下几个步骤:

  1. 初始化数据结构:创建一个栈(通常使用先进后出的原则)来存储待探索的节点,以及一个集合(通常使用哈希集合或集合)来记录已访问的节点。
  2. 将起始节点放入栈中,并将其标记为已访问。
  3. 进入循环,直到栈为空:
    • 从栈中取出一个节点。
    • 检查该节点是否为目标节点。如果是,则搜索完成,返回结果。
    • 如果不是目标节点,则将其所有未访问过的邻居节点加入栈,并标记为已访问。
    • 继续下一轮循环。
  1. 如果循环结束时仍未找到目标节点,则图中不存在目标节点。

剪枝:可以提前判断当前方案一定不合法,就不用往下搜

✨BFS

🍓宽搜流程图如下:

🍓宽搜流程:

🍓广搜模板

q.push(初始状态);
while(q.empty()){
	a=q.front();
    q.pop();
    for(枚举a的所有可达状态v){
        if(本状态v合法){
            执行标记操作;
            q.push(v);
        }
    }
}

连通块问题:

例题:全球变暖

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1005;
bool v[N][N],book[N*N];
char a[N][N];
int n,w[N][N],s,cnt;
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
typedef struct node
{
    int x,y;
}node;
queue<node>q;
bool check(int x,int y)
{
    if(x<1||x>n||y<1||y>n)
    return false;
    return true;
}
bool judge(int x,int y)
{
    if(check(x+1,y)&&a[x+1][y]=='.')
    return false;
    if(check(x,y+1)&&a[x][y+1]=='.')
    return false;
    if(check(x-1,y)&&a[x-1][y]=='.')
    return false;
    if(check(x,y-1)&&a[x][y-1]=='.')
    return false;
    return true;
}
void bfs()
{
    while(!q.empty())
    {
        node head,tail;
        head=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            tail.x=head.x+dx[i];
            tail.y=head.y+dy[i];
            if(check(tail.x,tail.y)&&a[tail.x][tail.y]=='#'&&w[tail.x][tail.y]==0)
            {
                w[tail.x][tail.y]=cnt;
                q.push(tail);
            }
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
        	cin>>a[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(a[i][j]=='#'&&w[i][j]==0)
            {
                cnt++;
                w[i][j]=cnt;
                node tmp={i,j};
                q.push(tmp);
                bfs();
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(a[i][j]=='#')
            {
                if(judge(i,j))
                {
                	book[w[i][j]]=true;
                }
            }
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        if(book[i]==true)
        {
        	s++;
        }
    }
    cout<<cnt-s;
	return 0;
}

问题2:

两个BFS

例题:Fire

/*
预处理:预处理出火传染到(i,j)点的最早时间 
人在去想要走到(i,j)点时,到(i,j)点的时刻一定要小于火最早到(i,j)的s时刻 
*/
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N=1000+5;
const int INF=0x3f3f3f3f;
typedef struct Node{
    int x,y;
    int t;
}Node;
int t,n,m;
int ti[N][N];//ti[i][j]是火最早到(i,j)的时间 
char a[N][N];
queue<Node> fq,q;
bool vis[N][N];
int _next[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool judge(int x,int y)
{
    if(x<1||x>n||y<1||y>m)
    return false;
    return true;
}
void FireBFS()
{
    Node _new;
    while(!fq.empty())
    {
        Node tp=fq.front();
        fq.pop();
        for(int i=0;i<4;i++)
        {
            _new.x=tp.x+_next[i][0];
            _new.y=tp.y+_next[i][1];
            if(judge(_new.x,_new.y)&&a[_new.x][_new.y]=='.'&&ti[_new.x][_new.y]==INF)
            {
                _new.t=tp.t+1;
                ti[_new.x][_new.y]=_new.t;
                fq.push(_new);
            }
        }
    }
}
int ManBFS(){
    Node _new;
    while(!q.empty()){
        Node tp=q.front();
        q.pop();
        if(tp.x==1||tp.x==n||tp.y==1||tp.y==m){
            return tp.t+1;
        }
        for(int i=0;i<4;i++){
            _new.x=tp.x+_next[i][0];
            _new.y=tp.y+_next[i][1];
            if(judge(_new.x,_new.y)&&a[_new.x][_new.y]=='.'&&!vis[_new.x][_new.y]){
            	_new.t=tp.t+1;
                if(_new.t<ti[_new.x][_new.y]){
                    vis[_new.x][_new.y]=true;
                    q.push(_new);
                }
            }
        }
    }
    return -1;
}
void init(){
    memset(ti,0x3f,sizeof(ti));
    memset(vis,false,sizeof(vis));
    while(!fq.empty())
    	fq.pop();
    while(!q.empty())
    	q.pop();
}
int main(){
    cin>>t;
    while(t--){
        init();
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cin>>a[i][j];
                if(a[i][j]=='F'){
                    Node tmp={i,j,0};
                    ti[i][j]=0;
                    fq.push(tmp);
                }
                else if(a[i][j]=='J'){
                    Node tmp={i,j,0};
                    vis[i][j]=true;
                    q.push(tmp);
            }
        }
    }
    FireBFS();
    int ans=ManBFS();
    if(ans==-1)
    	cout<<"IMPOSSIBLE"<<endl;
    else
    	cout<<ans<<endl;
    }
}
/* 
2 
4 4 
#### 
#JF# 
#..#
#..# 
3 3 
### 
#J. 
#.F 
*/

✨树与图

🍓树是特殊的图(连通无环的图)

🍓树与图的存储:

树是一种特殊的图,与图的存储方式相同。

对于无向图中的边ab,存储两条有向边a->b, b->a。

因此我们可以只考虑有向图的存储。

(1) 邻接矩阵:g[a][b] 存储边a->b

(2) 邻接表:

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;
// 添加一条边a->b
void add(int a, int b)
{
 e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
// 初始化
idx = 0;
memset(h, -1, sizeof h);

树与图的遍历:

时间复杂度 O(n+m)O(n+m), nn 表示点数,mm 表示边数

(1) 深度优先遍历

int dfs(int u)
{
     st[u] = true; // st[u] 表示点u已经被遍历过
     for (int i = h[u]; i != -1; i = ne[i])
     {
         int j = e[i];
         if (!st[j]) dfs(j);
     }
}

(2) 宽度优先遍历

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);
while (q.size())
{
     int t = q.front();
     q.pop();
     for (int i = h[t]; i != -1; i = ne[i])
     {
         int j = e[i];
         if (!st[j])
         {
             st[j] = true; // 表示点j已经被遍历过
             q.push(j);
         }
     }
}

🍓用宽搜框架来搜索图:

当使用宽度优先搜索(BFS)框架搜索图时,我们可以按照以下步骤进行操作

  1. 选择一个起始节点,并将其添加到队列中,同时将其标记为已访问。
  2. 重复以下步骤直到队列为空:
    • 从队列中取出一个节点作为当前节点。
    • 检查当前节点是否满足搜索条件。如果是,则返回结果或执行相应操作。
    • 遍历当前节点的所有相邻节点。
    • 对于每个未被访问的相邻节点,将其添加到队列中,并将其标记为已访问。
  1. 如果队列为空且没有找到满足条件的节点,则搜索结束,可以返回相应的结果或执行其他操作。

🍓以下是使用宽度优先搜索框架在C++中搜索图的示例代码:

#include <iostream>
#include <queue>
#include <unordered_set>
#include <unordered_map>
#include <vector>
bool bfs(const std::unordered_map<char, std::vector<char>>& graph, char startNode, char targetNode) {
    std::queue<char> queue;                      // 创建一个队列
    std::unordered_set<char> visited;            // 创建一个集合,用于记录已访问的节点
    queue.push(startNode);                        // 将起始节点放入队列
    visited.insert(startNode);                    // 标记起始节点为已访问
    while (!queue.empty()) {
        char node = queue.front();                 // 从队列中取出一个节点
        queue.pop();
        if (node == targetNode)                     // 检查是否为目标节点
            return true;
        const std::vector<char>& neighbors = graph.at(node);   // 获取当前节点的邻居节点
        for (char neighbor : neighbors) {
            if (visited.find(neighbor) == visited.end()) {   // 如果邻居节点未被访问过
                queue.push(neighbor);                        // 将邻居节点加入队列
                visited.insert(neighbor);                     // 标记邻居节点为已访问
            }
        }
    }
    return false;                                        // 循环结束,未找到目标节点
}
int main() {
    std::unordered_map<char, std::vector<char>> graph = {
        {'A', {'B', 'C'}},
        {'B', {'D', 'E'}},
        {'C', {'F'}},
        {'D', {}},
        {'E', {'F'}},
        {'F', {}}
    };
    char startNode = 'A';
    char targetNode = 'F';
    bool result = bfs(graph, startNode, targetNode);
    if (result)
        std::cout << "The target node '" << targetNode << "' is reachable from the start node '" << startNode << "'.\n";
    else
        std::cout << "The target node '" << targetNode << "' is not reachable from the start node '" << startNode << "'.\n";
    return 0;
}

在上述代码中,使用std::unordered_map来表示图的邻接表,其中键是节点,值是该节点的邻居节点列表。还使用std::queue来作为队列存储待探索的节点,std::unordered_set用于记录已访问的节点。

注意,上述代码仅为示例,假设图中的节点标识为字符('A', 'B', 'C'等),您可以根据实际情况进行修改和适应。

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

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

相关文章

第09讲:SkyWalking Agent 启动流程剖析,领略微内核架构之美

微内核架构 SkyWalking Agent 采用了微内核架构&#xff08;Microkernel Architecture&#xff09;&#xff0c;那什么是微内核架构呢&#xff1f;微内核架构也被称为插件化架构&#xff08;Plug-in Architecture&#xff09;&#xff0c;是一种面向功能进行拆分的可扩展性架构…

英文论文(sci)解读复现【NO.8】基于注意机制和感受野的YOLOv5在唐卡图像缺陷识别中的应用

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

【Unity100个实用小技巧】世界Canvas自动隐藏,包含子物体

☀️博客主页&#xff1a;CSDN博客主页&#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2;&#x1f525;学习专栏推荐&#xff1a;面试汇总❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&#…

【JavaEE】JUC(java.util.concurrent)的常见类以及线程安全的集合类

目录 1、JUC&#xff08;java.util.concurrent&#xff09;的常见类 1.1、Callable接口的用法&#xff08;创建线程的一种写法&#xff09; 1.2、ReentrantLock可重入互斥锁 1.2.1、ReentrantLock和synchronized的区别 1.2.2、如何选择使用哪个锁 1.3、Semaphore信号量 1…

pta(浙大第四版)五道经典练习题③

目录 ①7-4 IP地址转换 ②、查找日期 ③藏头词 四、IP地址转换 五、删除链表值为偶数的节点 ①7-4 IP地址转换 题述&#xff1a;IP地址转换&#xff1a;一个IP地址是用四个字节&#xff08;每个字节8个位&#xff09;的二进制码组成。输入32位二进制字符串&#xff0c;输…

探索iOS转场动画

iOS提供图像转场动画&#xff0c;可实现酷炫的转场特效。动画包括&#xff1a;溶解、折叠、复印机、暴露、翻页、波纹、滑动等等。 一、溶解动画 CIDissolveTransition提供溶解动画&#xff0c;我们来看看对应的转场动画效果&#xff1a; 在CIFilter指定CIDissolveTransition…

Qt线程基础,多线程使用注意点,目前支持的线程种类。

Qt线程基础 一、什么是线程&#xff1f;二、GUI线程和工作线程三、同时访问数据四、使用线程1、何时使用线程的替代品2、应该用哪种Qt线程技术&#xff1f; 六、Qt中的多线程技术1、QThread:带有可选事件循环的低级API2、QThreadPool和QRunnable:重用线程 七、Qt Concurrent:使…

集成学习以及随机森林介绍

一、集成学习简介 1.什么是集成学习&#xff1f; 集成学习&#xff08;Ensemble Learning&#xff09;是一种机器学习方法&#xff0c;通过将多个弱学习器&#xff08;weak learner&#xff09;组合在一起来构建一个更强大的学习器&#xff08;strong learner&#xff09;。 …

C语言进阶——字符函数和字符串函数(下)

在前面我们已经学习了strlen、strcpy、strcat、strcmp几个库函数&#xff0c;今天我们继续学习剩余的库函数。 上期链接&#xff1a; C语言进阶——字符函数和字符串函数&#xff08;上&#xff09;_wangjiushun的博客-CSDN博客 目录&#xff1a; 3、长度受限制的字符串函数…

Redis(四)持久化策略

文章目录 持久化策略1、为什么Redis需要持久化2、Redis提供的两种持久化方式(1)RGB持久化详解概述RGB持久化的两种触发策略手动触发实例测试&#xff1a;自动触发实例测试&#xff1a; 查看rdb的状态信息info Persistence rdb模式的优缺点 (2)AOF持久化详解AOF持久化步骤&#…

近期复盘 | 想多了都是问题,想开了都是答案

文章目录 &#x1f339;四月坚持背单词&#xff0c;五月坚持利用AI写文章&#x1f60a;六月会坚持干什么&#x1f64c;23年7月&#xff1a;毕业两年&#xff0c;参保两年&#x1f440;强制存储&#xff0c;消费降级&#xff0c;开源节流&#x1f61c;好好深耕能力&#x1f381;…

JavaScript 进阶 (一)

目录 作用域 局部作用域 函数作用域 块作用域 全局作用域 作用域链 JS垃圾回收机制 闭包 变量提升 函数进阶 函数提升 函数参数 箭头函数 基本语法 箭头函数参数 箭头函数this 解构赋值 数组解构 对象解构 遍历数组 forEach 方法&#xff08;重点&#xff09; …

shell SNAT与DNAT

文章目录 SNATSNAT原理与应用SNAT实验 DNATDNAT原理与应用DNAT实验 SNAT SNAT原理与应用 SNAT 应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff08;私有不能早Internet中正常路由&#xff09; SNAT原理&#xff1a;修改数据包的源地址。 SNAT转换前提…

文心一言 VS 讯飞星火 VS chatgpt (23)-- 算法导论4.2 5题

五、V.Pan 发现一种方法&#xff0c;可以用 132 464 次乘法操作完成 68 x 68 的矩阵相乘&#xff0c;发现另一种方法&#xff0c;可以用 143 640 次乘法操作完成 70 x 70 的矩阵相乘&#xff0c;还发现一种方法&#xff0c;可以用155 424次乘法操作完成 72 x 72 的矩阵相乘。当…

数据安全治理科技产品能力-数据安全复合治理框架和模型解读(2)

数据治理,数据安全治理行业在发展,在实践,所以很多东西是实践出来的,哪有什么神仙理论指导,即使有也是一家之说,但为了提高企业投产比,必要的认知是必须的,落地数据安全治理科技水平差异直接决定产品和项目是否可持续性,当前和未来更需要专业和有效创新。数据安全治理…

自动驾驶业内动态简讯

1. 引言 参与自动驾驶领域相关研发工作已有多年&#xff0c;针对该领域的快速发展&#xff0c;收集业内各大科技公司最新进展和技术突破&#xff0c;供伙伴们交流探讨。 闲话少说&#xff0c;直接开始吧! 2. 博世 据新闻介绍&#xff0c;博世在德国道路上测试L4级无人驾驶汽…

java 区分缺陷Defects/感染Infections/失败Failure

java 区分缺陷Defects/感染Infections/失败Failure 缺陷Defects 软件故障总是从代码中一个或多个缺陷的执行开始。 缺陷只是一段有缺陷、不正确的代码。 缺陷可能是程序语句的一部分或完整部分&#xff0c;也可能对应于不存在但应该存在的语句。 尽管程序员要对代码中的缺陷负…

基于SSM的甜品店商城系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着社会经济的发展和…

Altium Designer 相同电路多组复制布线

在进行设计开发的时候&#xff0c;总会遇到相同的电路&#xff0c;或者模块&#xff0c;这些电路可以使用相同的布局和走线。我们可以画好其中一部分&#xff0c;然后直接复制&#xff0c;就可以提高效率。下面记录我自己的实际操作过程&#xff0c;有一些地方遇到了问题&#…

Android | Android OS 源码结构

参考&#xff1a;AndroidXRef (http://androidxref.com/)版本&#xff1a;Pie - 9.0.0_r3 整体结构 对于 Android OS 的源码目录来说&#xff0c;各个版本的结构大同小异&#xff0c;随不同版本特性会有个别目录差异。编译后会额外产生一个 out 文件夹用于存储编译产生的文件。…