图论-DFS

从一开始的基础到慢慢进入数论,中间也算是写了一点点代码了。从今天开始就要开始进入图论了,不然一直在基础层面上,数据结果的知识一点都没有去进行涉及。虽然还有很多的内容没有更新完,但是现在还是开始去交叉的进行吧,之前上过数据结构的课,但是coding的能力很弱,现在补一下吧。也算是对这个知识点进行实际的操作吧。

DFS:深度优先搜索,尽量往深处进行,树的前中后遍历从某种意义上来说算是深度优先搜索
BFS:广度优先搜索,从广度上进行出发(类比于水),数的层序遍历其实就是类似于BFS。

这里用树去进行类比

从数据结构上看:DFS使用栈stack,BFS使用的是队列
从空间复杂度上看:DFS的时间复杂度O(n),BFS的时间复杂度为O( 2 n 2^n 2n)

对比:
BFS存在最短路的概念,因为是按层去进行查找。
DFS不具有最短路的性质,因为是从深度从进行出发。

DFS和BFS可能涉及回溯和剪枝

引入例题:

#include <iostream>
#include <vector>
using namespace std;
//DFS俗称暴力搜索,+回溯,回溯之后一定要恢复现场 
const int N=10;
//记录方案 
int path[N];//记录树的路径 
int n;
bool st[N];//表示哪些数被用掉了,用掉了就是true 。
//此时st[N]都是false 
void dfs(int u){
	if(u==n)//此时表示所有的位置填满了 
	{
		for(int i=0;i<n;i++){
			cout<<path[i]<<" ";
		}
		puts("");//换行 
		return;
	}
	//说明没有访问到最底层,数字没有填充完 
	for(int i=0;i<n;i++){
		//!st[i] :表示数没有被用过 
		if(!st[i]){
			path[u]=i;
			st[i]=true;//标示的是数字i被用过了 
			dfs(u+1);//递归到下一层 
			st[i]=false;//回溯的过程、这个很关键 
		}
	}
} 

#include <iostream>
using namespace std;
int main(){
	
	cin>>n;
	dfs(0);//从第0开始看,也就是根节点 
	return 0;
}

DFS的另一个应用:N皇后的问题,顺便复习一下递归。
引入例题:
Acwing-843. n-皇后问题
leetcode-51. N皇后
其实这两个题目一模一样:
这里通过三个方法:
前两个方法采用深度优先搜索:
就是稍微格式上改了一点

#include <iostream>
using namespace std;
const int N=20;
int n;
bool col[N],N_dg[N],P_dg[N];
char oc[N][N];

//深搜的过程
void dfs(int u){
    if(u==n){
        for(int i=0;i<n;i++){
            puts(oc[i]);
        }
        puts("");
        return;
    }
    for(int i=0;i<n;i++){
        //回溯+剪枝
        if(!col[i]&&!N_dg[u+i]&&!P_dg[n-u+i]){
            oc[u][i]='Q';
            col[i]=N_dg[u+i]=P_dg[n-u+i]=true;
            dfs(u+1);
            col[i]=N_dg[u+i]=P_dg[n-u+i]=false;
            oc[u][i]='.';
        }
    }
}
int main(){
    cin>>n;   
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            oc[i][j]='.';
        }           
    }
    //进行dfs深搜
    dfs(0);
    return 0;
}

leetcode的题解:

如何从行列去进行深搜呢?常规思路,就像是我们下期一样。

#include <iostream>
using namespace std;
const int N=20;

bool col[N],row[N],N_dg[N],P_dg[N]; 
char oc[N][N];
int n;//n表示皇后的个数,多少行就存在多少个皇后 
//s标示的是皇后的个数 
void dfs(int x,int y,int s){
	//两种边界条件假设 
	if(y==n) y=0,x++;
	if(x==n){
		if(s==n){
			for(int i=0;i<n;i++){
				puts(oc[i]);
			}
			puts("");
		}
		return;
	} 
	
	//基于两种选择,放皇后和不放皇后 
	
	//不放皇后,递归到下一个格子,也就是下一列 
	dfs(x,y+1,s);
	//放皇后 .条件:这一行,这一列,对角线都不允许放皇后 
	if(!row[x]&&!col[y]&&!N_dg[x+y]&&!P_dg[x-y+n]){
		oc[x][y]='Q';
		//条件成立,更新条件,放皇后
		row[x]=col[y]=N_dg[x+y]=P_dg[x-y+n]=true;
		//递归到下一层
		dfs(x,y+1,s+1);
		row[x]=col[y]=N_dg[x+y]=P_dg[x-y+n]=false;
		oc[x][y]='.'; 
	} 
}
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			oc[i][j]='.';//为了符合格式将每一个二维数组全部填成. 
		}
	}
	//从左上角开始去进行搜索,并记录有多少个皇后 
	//前两个参数为某行某列,第三个参数是皇后个数 
	dfs(0,0,0);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
桥问题是图论中一个经典的问题,也称为割边问题。给定一个无向连通图,求其中所有的桥,即删除这条边后,图不再连通的边。下面是朴素算法+邻接表实现桥问题的C++代码: ```c++ #include <bits/stdc++.h> using namespace std; const int MAXN = 10010; int dfn[MAXN], low[MAXN], cnt; bool bridge[MAXN]; vector<int> G[MAXN]; void dfs(int u, int fa) { // u是当前节点,fa是u的父亲节点 dfn[u] = low[u] = ++cnt; // 第一次访问u,dfn[u]和low[u]都赋为cnt for (int i = 0; i < G[u].size(); ++i) { // 枚举u的所有邻接点 int v = G[u][i]; if (!dfn[v]) { // 如果v还没有被访问过 dfs(v, u); low[u] = min(low[u], low[v]); // 更新u的low值 if (low[v] > dfn[u]) bridge[v] = true; // 如果(u, v)是桥,标记v } else if (v != fa) // 如果v已经被访问过,且不是u的父亲节点 low[u] = min(low[u], dfn[v]); // 更新u的low值 } } int main() { int n, m; cin >> n >> m; for (int i = 1; i <= m; ++i) { int u, v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } for (int i = 1; i <= n; ++i) if (!dfn[i]) dfs(i, 0); for (int i = 1; i <= n; ++i) if (bridge[i]) cout << i << " "; cout << endl; return 0; } ``` 其中,`dfn[u]`表示节点u被遍历到的时间戳,`low[u]`表示节点u能够回溯到的最早的时间戳(即u能够到达的最高祖先的时间戳),`bridge[i]`表示节点i是否为桥。在dfs过程中,如果当前节点v还没有被访问过,则继续往下遍历,将v作为新的节点u,更新u的low值。如果v已经被访问过,且不是u的父亲节点,则说明(u, v)是反向边,更新u的low值即可。如果low[v] > dfn[u],则说明(u, v)是正向边,且(v, u)不是反向边,即(u, v)是桥,标记v即可。最后输出所有的桥即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值