图的DFS和BFS-------(基于邻接表)以及拓扑排序

深入研究DFS算法:

转载:

代码模板:

静态链表:

深度优先搜索DFS

宽度优先搜索BFS

王道:

例题 :

Leetcode046全排列(不含重复元素)-----------回溯法简单应用

描述:

​ 代码:

题解:

Acwing843(n-皇后问题)--------DFS(全排列)

描述:

代码:

题解:

Acwing846树的重心---------dfs(邻接表)

描述:

代码:

题解:

Acwing844走迷宫(最短路问题)----------(bfs)

描述:

代码:

题解:

Acwing847图中点的层次-------bfs

描述:

代码:

题解:

Acwing845(8数码)--------bfs

描述:

代码:

题解:

Acwing848有向图的拓扑序列---------bfs

描述:

代码:

题解:


深入研究DFS算法:

https://blog.csdn.net/qq_52934831/article/details/119256878?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023677416780262550476%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023677416780262550476&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-1-119256878.pc_v2_rank_blog_default&utm_term=%E5%9B%9E%E6%BA%AF&spm=1018.2226.3001.4450

转载:

https://blog.csdn.net/qq_39328436/article/details/106956963?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163012979216780271520735%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163012979216780271520735&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-4-106956963.pc_v2_rank_blog_default&utm_term=%E6%A0%91%E7%9A%84%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88&spm=1018.2226.3001.4450

代码模板:

静态链表:

深度优先搜索DFS

#include <iostream>
#inclue  <cstring>
#include <algorithm>
using namespace std;
const int N=10010,M=N*2;
//因为有N个头节点所以定义N个数组,单链表可以直接int h
int h[N],e[M],ne[M],idx;
bool stu[N];
void add(int a,int b){
//插入操作即在h[a]头结点后插入节点b
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//可以自定义从哪个节点开始搜索
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);
    }
}
int main(){
//初始化让所有的头结点指向空节点(下标为-1)
memset(h,-1,sizeof h);
dfs(1);
}

宽度优先搜索BFS

#include <iostream>
#inclue  <cstring>
#include <algorithm>
using namespace std;
const int N=10010,M=N*2;
//因为有N个头节点所以定义N个数组,单链表可以直接int h
int h[N],e[M],ne[M],idx;
bool stu[N];
queue<int> q;
void add(int a,int b){
//插入操作即在h[a]头结点后插入节点b
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void bfs(){
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);
        }
    }
}
}
int main(){
//初始化让所有的头结点指向空节点(下标为-1)
memset(h,-1,sizeof h);
bfs(1);
}

王道:

/*边表节点*/
typedef struct ArcNode{
	int adjvex;//弧指向的节点下标
	struct ArcNode *next;	//指向下一条弧的指针
}ArcNode;
 
 
/*顶点表节点*/
typedef char vertexType ;
typedef struct VNode {
	vertexType data;
	ArcNode* first;//指向一个边表节点
}VNode,AdjList[100];//顶点数组
 
 
/*图*/
typedef struct {
	AdjList vertices;
	int vecnum, arcnum;
}ALGraph;
 
 
vector<bool> visited(100,false);
 
void DFS_DG(ALGraph G, int i) {
	cout << G.vertices[i].data << " ";
	visited[i] = true;
	for (ArcNode* p = G.vertices[i].first; p; p = p->next) {
        //p->adjvex弧指向的节点下标
		if (!visited[p->adjvex])DFS_DG(G, p->adjvex);
	}
}
 
void DFS(ALGraph G, int i) {
	stack<int> s;//只要进栈就要修改标记,保证所有的节点只有一次机会入栈,这样遍历节点才不会重复
	int v;
	s.push(i);
	visited[i] = true;
	while (!s.empty())
	{
		v = s.top();
		s.pop();	
		cout << G.vertices[v].data << " ";//出栈时访问
		for (ArcNode* p = G.vertices[v].first; p; p = p->next) {
			if (!visited[p->adjvex]) {
				s.push(p->adjvex);//栈中存放的是还未访问的
				visited[p->adjvex] = true;
			}
		}
	}
}
 
void BFS(ALGraph G, int i) {
	queue<int> q;
	cout << G.vertices[i].data << " ";
	q.push(i);
	visited[i] = true;
	int v;
	while (!q.empty())
	{
		v = q.front();
		q.pop();
		for (ArcNode* p = G.vertices[v].first; p; p = p->next) {
			if (!visited[p->adjvex]) {
				cout << G.vertices[p->adjvex].data << " ";
				visited[p->adjvex] = true;
				q.push(p->adjvex);//队列中存放的是已经访问过的
			}
		}
	}
}
 
void Traverse() {
	int n, m;
	cout << "请输入有向图顶点数和边数" << endl;
	cin >> n >> m;
	ALGraph G;
	create(&G, n, m);
	cout << "递归深度搜索:" << endl;
	for (int i= 0; i < G.vecnum; i++) {//如果没有这个给循环,非连通的图就只能深度遍历一个连通分量
		if (!visited[i]) DFS_DG(G, i);
	}
 
	for (int i = 0; i < G.vecnum; i++) visited[i] = false;//将全局数组修改为原来的样子
	cout << endl;
	cout << "非递归深度搜索:" << endl;
	for (int i = 0; i < G.vecnum; i++) {
		if (!visited[i]) DFS(G, i);
	}
 
	for (int i = 0; i < G.vecnum; i++) visited[i] = false;//将全局数组修改为原来的样子
	cout << endl;
	cout << "广度搜索:" << endl;
	for (int i = 0; i < G.vecnum; i++) {
		if (!visited[i]) BFS(G, i);
	}
}
 
 
int main()
{
	
	Traverse();
}

例题 :

Leetcode046全排列(不含重复元素)-----------回溯法简单应用

描述:

 代码:

#include <iostream>
using namespace std;
int n;
//1<=n<=7
const int N=8;
bool exist[N];
int temp[N];
//len代表在第几层
void dfs(int len){
    if(len==n){
        //依次输出每层上的数
        for(int i=0;i<n;i++) cout<<temp[i]<<" ";
        cout<<endl;
        return;
    }
    for(int i=1;i<=n;i++){
        if(!exist[i]){
            exist[i]=true;
            temp[len]=i;
            dfs(len+1);
            temp[len]=0;
            exist[i]=false;
        }
    }
}
int main(){
    cin>>n;
    dfs(0);
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119570812

Acwing843(n-皇后问题)--------DFS(全排列)

描述:

代码:

#include <iostream>
using namespace std;
int n;
//1<=n<=9
const int N=10;
char qi[N][N];
//分别记录的是该位置的列,正对角线,斜对角线上是否放过皇后,若均没有皇后,填入皇后,并递归到下一行
//为什么正对角和反对角要2*N,因为列只有N条,而对角线有2*N条
bool lie[N],z[2*N],f[2*N];
//当前枚举的是第x行的Q应该放在第y列?
void dfs(int x){
    //此时Q已经全部放完
    if(x==n){
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++)
                cout << qi[i][j];
            cout << endl;
        }
        cout << endl;
        return;
    }
    //y表示第y列
    for(int y=0;y<n;y++){
        //x-y+n和x+y怎么得来的见图解
        if(!lie[y]&&!z[x-y+n]&&!f[x+y]){
            lie[y]=z[x-y+n]=f[x+y]=true;
            //要是它的同一条列,正对角和反对角都没有皇后,那么就在第x行第y列放上皇后
            qi[x][y]='Q';
            //递归去求下一行
            dfs(x+1);
            //回溯
            lie[y]=z[x-y+n]=f[x+y]=false;
            qi[x][y]='.';
        }
    }
}
int main(){
    cin>>n;
    //初始化棋盘
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            qi[i][j]='.';
        }
    }
    dfs(0);
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119924590?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023619916780274191989%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023619916780274191989&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-4-119924590.pc_v2_rank_blog_default&utm_term=dfs&spm=1018.2226.3001.4450

Acwing846树的重心---------dfs(邻接表)

描述:

代码:

#include <cstring>
#include <iostream>
#include <climits>
using namespace std;
//首先写出dfs的模板
const int N=1e5+10;
//因为是无向图,所以每个节点至多对应2n-2条边
const int M=N*2;
//节点个数
int n;
int h[N],e[M],ne[M],idx;
bool stu[N];
//ans存储的是将重心删除后剩余各个连通块中点数的最大值
//根据重心的定义,将中心删除后,剩余各个连通块中点数的最大值最小,所以ans初始值要尽可能取大
int ans=INT_MAX;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//返回的是以u为根节点的子树大小
int dfs(int u){
    stu[u]=true;
    //sum记录当前以u为根的子树大小,res记录删除重心后各个联通块的最大值
    int sum=1,res=0;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!stu[j]){
            int s=dfs(j);//以j为根的子树大小
            sum+=s;//将该子树添加到sum中,来计算出以u为根的子树完整大小
            //以j为根的子树是以u为根的删除u后的联通块,所以要与res进行比较取出最大值
            res=max(res,s);
        }//if
    }//for
    //n-sum表示的是减掉u为根的子树,整个树剩下的点的数量
    res=max(res,n-sum);
    ans=min(res,ans);
    return sum;
}
int main(){
    //首先初始化使节点都指向空
    memset(h,-1,sizeof h);
    cin>>n;//由题得,边数=n-1;
    for(int i=0;i<n-1;i++){
        int a,b;
        cin>>a>>b;
        //因为是无向图,所以两个方向都要加
        add(a,b),add(b,a);
    }
    dfs(1);//节点可任意选
    cout<<ans;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119977634?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023619916780274191989%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023619916780274191989&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-3-119977634.pc_v2_rank_blog_default&utm_term=dfs&spm=1018.2226.3001.4450

Acwing844走迷宫(最短路问题)----------(bfs)

描述:

代码:

#include <iostream>
#include <cstring>
//因为bfs需要借助queue,所以加上头文件
#include <queue>
using namespace std;
int n,m;
const int N=1e2;
int g[N][N];//存放迷宫数组,来表示通或不通
//d[][]有两种用处:1、初始化为-1,来表示没有走过这个点,走过就更新为0
//2、保存该点与起点的距离
int d[N][N];
queue <pair<int,int>>q;
//注意一一对应
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
//返回从左上角移动至右下角的最少移动次数
int bfs(){
    //首先将d[n][m]初始化为-1,表示没有走过
    //注意使用memset函数初始化只能初始化为0或者-1
    memset(d,-1,sizeof d);
    //把起点入队,并将其状态改为0,表示走过一次
    q.push({0,0});
    d[0][0]=0;
    while(!q.empty()){
        //记录队头元素
        auto temp=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            //从队头这个点,尝试往四个方向走,例如i=0
            //x=temp.first-1,y=temp.second+0;
            int x=temp.first+dx[i];
            int y=temp.second+dy[i];
            //尝试去走的这个点,没有超过边界,迷宫里这里是可以走的,且没走过,加入队头
            if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]==-1){
                //这个点到起点的距离更新为上个点到起点的距离+1
                d[x][y]=d[temp.first][temp.second]+1;
                q.push({x,y});
            }
        }
    }
    //返回终点到起点的距离
    return d[n-1][m-1];
}
int main(){
    cin>>n>>m;
    //将迷宫导入
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>g[i][j];
        }
    }
    cout<<bfs();
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119952424?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023646816780271556223%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023646816780271556223&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-4-119952424.pc_v2_rank_blog_default&utm_term=bfs&spm=1018.2226.3001.4450

Acwing847图中点的层次-------bfs

描述:

代码:

#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m;//n个点和m条边
const int N=1e5+10;
int d[N];
int h[N],e[N],ne[N],idx;
queue<int> q;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int bfs(){
    //首先将d[N]都初始化为-1,表示没遍历过
    memset(d,-1,sizeof d);
    d[1]=0;
    q.push(1);
    while(!q.empty()){
        int temp=q.front();
        q.pop();
        //拓展
        for(int i=h[temp];i!=-1;i=ne[i]){
            int j=e[i];
            //该点没被遍历过
            if(d[j]==-1){
                d[j]=d[temp]+1;
                q.push(j);
        }
    }
}
return d[n];
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof d);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    cout<<bfs();
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119980197?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023646816780271556223%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023646816780271556223&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-3-119980197.pc_v2_rank_blog_default&utm_term=bfs&spm=1018.2226.3001.4450

Acwing845(8数码)--------bfs

描述:

代码:

#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
//保存字符串=>代表了当前棋盘的状态
queue<string> q;
//保存从起点到当前状态需要几步
unordered_map<string,int> d;
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
int bfs(string  start){
    //当字符串等于end时,说明整个棋盘也就是达到了最终的状态
    string end="12345678x";
    //bfs模板
    q.push(start);
    d[start]=0;
    while(!q.empty()){
        auto t=q.front();
        q.pop();
        int distance=d[t];
        if(t==end) return distance;
        //状态转移即x移动的过程
        //首先找到x在字符串中位置
        int l=t.find('x');
        //将在字符串中位置转化为在棋盘的位置
        int x=l/3,y=l%3;
        for(int i=0;i<4;i++){
            int a=x+dx[i],b=y+dy[i];
            if(a>=0&&a<3&&b>=0&&b<3){
                //将x与它移动到的字符进行交换
                swap(t[l],t[a*3+b]);
                //假如交换后的字符串没有在之前出现过
                if(!d.count(t)) {
                    //这时已经交换了,虽然写的任然是t但是已经和之前不一样了
                    d[t]=distance+1;
                    q.push(t);
                }//if
                //注意这里一定要恢复原位,比如说i=0=>即向上走,之后一定要记得再次交换
                //只有这样才能向下走一位回到原来的位置,这时再取i=1,向左走
                swap(t[l],t[a*3+b]);
            }//if
        }//for
    }//while
    return -1;//找不到
}
int main(){
    //输入初始状态的字符串
    string start;
    for(int i=0;i<9;i++){
        char c;
        cin>>c;
        start+=c;
    }
    cout<<bfs(start);
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119959719?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023646816780271556223%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023646816780271556223&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-2-119959719.pc_v2_rank_blog_default&utm_term=bfs&spm=1018.2226.3001.4450

Acwing848有向图的拓扑序列---------bfs

描述:

代码:

#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m;
const int N=1e5+10;
queue<int> q;
//d[N]存储节点的入度,这里不记录是否被遍历过!
int d[N];
int h[N],e[N],ne[N],idx;
//top[N]存储所有入度为0的节点,数组的存储顺序就是拓扑序列的顺序,sum是已经在top[N]里的节点数
int top[N],num=1;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
    //找到入度为0的节点入队
    for(int i=1;i<=n;i++){
        if(d[i]==0) q.push(i);
    }
    while(!q.empty()){
        int temp=q.front();
        q.pop();
        top[num++]=temp;
        for(int i=h[temp];i!=-1;i=ne[i]){
            int j=e[i];
            //j节点的入度-1
            d[j]--;
            if(d[j]==0) q.push(j);
        }
    }
    //这里num初值是1, 后面num++了, 所以队列中元素是n的时候,num== n +1
    //假如存在拓扑序列,那么所有点的入度都应该为0,top[N]应该包含所有节点,即num==n+1;
    return num==n+1;
}
int main(){
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        d[b]++;
    }
    if(topsort()){
        for(int i=1;i<=n;i++){
            cout<<top[i]<<" ";
        }
    }
    else cout<<-1<<endl;
    return 0;
}

题解:

https://blog.csdn.net/qq_52934831/article/details/119982016?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163023646816780271556223%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=163023646816780271556223&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_v29-5-119982016.pc_v2_rank_blog_default&utm_term=bfs&spm=1018.2226.3001.4450

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值