代码随想录算法训练营第五十天|图论理论基础、深搜、广搜

图论理论基础

图的基本概念:二维坐标中,两点可以连成线,多个点连成的线就构成了图。当然图也可以就一个节点,甚至没有节点(空图)。

图的种类:有向图(图中边是有方向的)和无向图(图中边没有方向)。

加权有向图(图中边是有权值的),加权无向图同理。

无向图中有几条边连接该节点,该节点就有几度。

有向图中每个节点有出度和入度。出度:从该节点出发的边的个数。入度:指向该节点边的个数。

连通性:在图中表示节点的连通情况,我们称之为连通性。

连通图:在无向图中,任何两个节点都是可以到达的,我们称之为连通图 。如果有节点不能到达其他节点,则为非连通图。

强连通图:在有向图中,任何两个节点是可以相互到达的,我们称之为 强连通图。

连通分量:在无向图中的极大连通子图称之为该图的一个连通分量。

强连通分量:在有向图中极大强连通子图称之为该图的强连通分量。

图的构造:一般使用邻接表、邻接矩阵 或者用类来表示。主要是 朴素存储、邻接表和邻接矩阵。

邻接矩阵:邻接矩阵 使用 二维数组来表示图结构。 邻接矩阵是从节点的角度来表示图,有多少节点就申请多大的二维数组。

例如: grid[2][5] = 6,表示 节点 2 连接 节点5 为有向图,节点2 指向 节点5,边的权值为6。

如果想表示无向图,即:grid[2][5] = 6,grid[5][2] = 6,表示节点2 与 节点5 相互连通,权值为6。

邻接表:邻接表 使用 数组 + 链表的方式来表示。 邻接表是从边的数量来表示图,有多少边 才会申请对应大小的链表。

图的遍历方式:深度优先搜索(dfs)、广度优先搜索(bfs)。

深度优先搜索理论基础

深搜与广搜的区别:

  • dfs深搜是可一个方向去搜,不到黄河不回头,直到遇到绝境了,搜不下去了,再换方向(换方向的过程就涉及到了回溯)。
  • bfs广搜是先把本节点所连接的所有节点遍历一遍,走到下一个节点的时候,再把连接节点的所有节点遍历一遍,搜索方向更像是广度,四面八方的搜索过程。

深搜三部曲

1.确认递归函数、参数。

2.确定终止条件。

3.处理目前搜索节点出发的路径。

vector<vector<int>> result; // 保存符合条件的所有路径
vector<int> path; // 起点到终点的路径
void dfs(图,目前搜索的节点){
   if (终止条件) {
     存放结果;
     return;
  }
  for (选择:本节点所连接的其他节点) {
    处理节点;
    dfs(图,选择的节点); // 递归
    回溯,撤销处理结果
  }
}

所有可达路径 卡码网 98

写法一:邻接矩阵写法

#include <iostream>
#include <vector>
using namespace std;

vector<vector<int>> res;
vector<int> path;

void dfs(const vector<vector<int>> &graph,int x,int n){
    if(x==n){
        res.push_back(path);
        return;
    }
    for(int i=1;i<=n;i++){
        if(graph[x][i]==1){
            path.push_back(i);
            dfs(graph,i,n);
            path.pop_back();
        }
    }
}

int main()
{
    int N,M,s,t;
    cin>>N>>M;
    
    vector<vector<int>> graph(N+1,vector<int>(N+1,0));
    while(M--){
        cin>>s>>t;
        graph[s][t]=1;
    }
    path.push_back(1);
    dfs(graph,1,N);
    
    if(res.size()==0) cout<<-1<<endl;
    for(const vector<int> &pa:res){
        for(int i=0;i<pa.size()-1;i++){
            cout<<pa[i]<<" ";
        }
        cout<<pa[pa.size()-1]<<endl;
    }
}

写法二:邻接表写法

#include <iostream>
#include <vector>
#include <list>
using namespace std;

vector<vector<int>> res;
vector<int> path;

void dfs(const vector<list<int>> &graph,int x,int n){
    if(x==n){
        res.push_back(path);
        return;
    }
    for(int i:graph[x]){
        path.push_back(i);
        dfs(graph,i,n);
        path.pop_back();
    }
}

int main()
{
    int N,M,s,t;
    cin>>N>>M;
    
    vector<list<int>> graph(N+1);
    while(M--){
        cin>>s>>t;
        graph[s].push_back(t);
    }
    path.push_back(1);
    dfs(graph,1,N);
    
    if(res.size()==0) cout<<-1<<endl;
    for(const vector<int> &pa:res){
        for(int i=0;i<pa.size()-1;i++){
            cout<<pa[i]<<" ";
        }
        cout<<pa[pa.size()-1]<<endl;
    }
}

总结

1.使用邻接矩阵和邻接表存放图。

2.ACM格式输入输出。

广度优先搜索理论基础

广搜(bfs)是一圈一圈的搜索过程,和深搜(dfs)是一条路跑到黑然后再回溯。

广搜(bfs)使用场景:解决两个点之间的最短路径问题。

也有一些问题是广搜 和 深搜都可以解决的,例如岛屿问题,这类问题的特征就是不涉及具体的遍历方式,只要能把相邻且相同属性的节点标记上就行

代码框架:

int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
    queue<pair<int, int>> que; // 定义队列
    que.push({x, y}); // 起始节点加入队列
    visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
    while(!que.empty()) { // 开始遍历队列里的元素
        pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
        int curx = cur.first;
        int cury = cur.second; // 当前节点坐标
        for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
            int nextx = curx + dir[i][0];
            int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 坐标越界了,直接跳过
            if (!visited[nextx][nexty]) { // 如果节点没被访问过
                que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点
                visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
            }
        }
    }

}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值