深度优先遍历(DFS)
不撞南墙不回头算法
撞了之后就回退一步找别的路(回溯算法)
直到访问所有的顶点
注意:深度优先遍历的序列不是唯一的
若无向图非连通:
需要执行多次深度优先遍历
重要结论:对于非连通无向图,执行几次深度优先搜索,图中就有几个连通分量
回忆一下回溯算法模板
void backtracking(参数)
{
if(终止条件)
{
存放结果;
return;
}
for(选择:本层集合中的元素)
{
处理结点;
backtracking(路径,选择列表);
回溯,撤销结果
}
}
dfs代码框架:
void dfs(参数)
{
if(终止条件)
{
存放结果;
return;
}
for(选择:本结点所连接的其他结点)
{
处理结点;
dfs(图,处理的结点);
回溯,撤销结果
}
}
深搜三部曲:
1.确认递归函数,参数
void dfs(参数);
举例:
vector<vector<int>>result;
vector<int>path;
void dfs(图,目前搜索的结点)
2.确认终止条件
if(终止条件)
{
存放结果;
return;
}
3.处理当前搜索结点出发的路径
for(选择:本结点所连接的结点)
{
处理结点;
dfs(图,选择的结点);
回溯,撤销处理的结果
}
1.所有可达路径
【题目描述】
给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。
【输入描述】
第一行包含两个整数 N,M,表示图中拥有 N 个节点,M 条边
后续 M 行,每行包含两个整数 s 和 t,表示图中的 s 节点与 t 节点中有一条路径
【输出描述】
输出所有的可达路径,路径中所有节点的后面跟一个空格,每条路径独占一行,存在多条路径,路径输出的顺序可任意。
如果不存在任何一条路径,则输出 -1。
注意输出的序列中,最后一个节点后面没有空格! 例如正确的答案是
1 3 5
,而不是1 3 5
, 5后面没有空格!数据范围:
图中不存在自环
图中不存在平行边
1 <= N <= 100
1 <= M <= 500
重点练习邻接矩阵和邻接表的使用方法以及深搜模板
**邻接矩阵**:
//结点编号从1开始,为了结点标号与下标对齐,申请(n+1)*(n+1)的二维数组
vector<vector<int>>graph(n+1,vector<int>(n+1,0));
//输入m个边
while(m--)
{
cin>>s>>t;
//邻接矩阵,1表示s指向t
graph[s][t]=1;
}
**邻接表**
//邻接表=数组+链表,根据边的数量来表示图,有多少边就申请对应多大的链表
//结点编号从1到n,申请n+1大小的数组
vector<list<int>>graph(n+1);//list为c++中的链表
//输入m个边
while(m--)
{
cin>>s>>t;
//使用邻接表,表示s->t是相连的
graph[s].push_back(t);
}
代码:
#include <iostream>
#include <list>
#include <vector>
using namespace std;
vector<vector<int>>result;//存储所有的路径
vector<int>path;//存储单条路径
void dfs(const vector<vector<int>>&graph,int cur,int end)
{
//确定终止条件
if(cur==end)//抵达终点
{
result.push_back(path);
return;
}
//循环
for(int i=1;i<=end;i++)//遍历当前结点cur链接的所有结点
{
if(graph[cur][i]==1)//连上了
{
path.push_back(i);//加入路径
dfs(graph,i,end);//进入下一层递归
path.pop_back();//撤销操作
}
}
}
int main()
{
int N,M;
int 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);//无论什么路径都已经是从0结点出发
dfs(graph,1,N);
//输出结果
if(result.size()==0)cout<<-1<<endl;
for(const vector<int>&pa:result)
{//输出二维数组结果中的一维数组路径
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>>result;
vector<int>path;//节点1到终点的路径
void dfs(const vector<list<int>>&graph,int cur,int end)
{
if(cur==end)
{
result.push_back(path);
return;
}
for(int i:graph[cur])//找到cur指向的节点
{
path.push_back(i);
dfs(graph,i,end);
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);//从1开始
dfs(graph,1,n);
if(result.size()==0)cout<<-1<<endl;
for(const vector<int>&pa:result)
{
for(int i=0;i<pa.size()-1;i++)
{
cout<<pa[i]<<" ";
}
cout<<pa[pa.size()-1]<<endl;
}
return 0;
}
广度优先遍历(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;//立即标记为已访问
}
}
}
}
期末好忙,完全没空补dp