搜索和图论之DFS、BFS和拓扑排序

1.DFS

时间复杂度 O ( n + m ) O(n+m) O(n+m)

例题 846. 树的重心 - AcWing题库

题目概述

找出树的重心,重心是一个节点,删除该结点后可以使得剩余连通图中点数的最大最小

解题思路

( 1 ) (1) (1) 每个节点在遍历时return: 子节点个数 + 1 子节点个数+1 子节点个数+1

( 2 ) (2) (2) 每个节点在遍历时可计算更新: m a x ( 各个子树的节点的最大值 , 节点总数 − ( 子节点 + 1 ) ) max(各个子树的节点的最大值,节点总数-(子节点+1)) max(各个子树的节点的最大值,节点总数(子节点+1))

完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;  //节点数量
const int M=2*N;  //边的数量
int h[N],e[M],ne[M],idx,re[N],tmax,tsize; 
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u)
{
	re[u]=1;
	int size=1,curmax=0;
	for(int i=h[u];i!=-1;i=ne[i])
	{
		if(re[e[i]]==0) 
		{
			int sonsize=dfs(e[i]);
			size+=sonsize;
			curmax=max(curmax,sonsize);
		}
	}
	curmax=max(curmax,tsize-size);
	if(curmax<tmax) tmax=curmax;
	return size;
}
int main()
{
	int a,b;
	idx=0;
	memset(h,-1,sizeof(h));
	memset(re,0,sizeof(re));
	cin>>tsize;
	tmax=tsize;
	for(int i=1;i<tsize;i++)
	{
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	dfs(1);
	cout<<tmax;
	return 0;
}

2.BFS

时间复杂度 O ( m + n ) O(m+n) O(m+n)

例题 847. 图中点的层次 - AcWing题库

题目概述

所有边的长度为 1 1 1,求 1 − n 1-n 1n 的最短路径

解题思路

d [ j ] d[j] d[j] 表示 1 − j 1-j 1j 的距离初始化为 ∞ \infty

完整代码
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+10;  //节点数量
const int M=2*N;  //边的数量
int h[N],d[N],e[M],ne[M],idx=0,re[N]; 
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int u)
{
	queue<int> q;
	re[u]=1;
	q.push(u);
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		for(int i=h[t];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(!re[j])
			{
				re[j]=1;
				d[j]=d[t]+1;
				q.push(j);
			}
		}
	}
	
}
int main()
{
	int n,m,a,b;
	cin>>n>>m;
	memset(h,-1,sizeof h);
	memset(d,INF,sizeof d);
	d[1]=0;
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b;
		add(a,b);
	}
	bfs(1);
	if(d[n]==INF) cout<<-1;
	else cout<<d[n];
	return 0;
}

3.拓扑排序

定义:拓扑序列中所有节点都是从前指向后(针对有向图)

有向无环图一定存在拓扑序列,因而有向无环图也被成为拓扑图

例题 848. 有向图的拓扑序列 - AcWing题库

题目概述

求一有向图的拓扑序列

解题思路

( 1 ) (1) (1) 利用 d [ j ] d[j] d[j] 记录 j j j 节点的入度

( 2 ) (2) (2) 数组模拟队列: h h = 0 , t t = 0 hh=0,tt=0 hh=0,tt=0

q [ t t + + ] = ? q[tt++]=? q[tt++]=? 表示入队 q [ + + h h ] = ? q[++hh]=? q[++hh]=? 表示出队

( 3 ) (3) (3) 把入度为 0 0 0 的都入队,弹出队头元素,遍历其所有连接的节点,删除连接的边(即 d [ j ] − − d[j]-- d[j]),即只要 d [ j ] = = 0 d[j]==0 d[j]==0 把其压入队列

( 4 ) (4) (4) return t t − n tt-n ttn

​ 如果存在环,不会进入队列,则 t t − n < 0 tt-n<0 ttn<0

完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=2*N;
int h[N],e[M],ne[M],idx,d[N],q[N],n,m;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort()
{
	int hh=0,tt=0;
	for(int i=1;i<=n;i++)
	{
		if(d[i]==0)
			q[tt++]=i;
	}
	while(hh<tt)
	{
		int u=q[hh++];
		for(int i=h[u];i!=-1;i=ne[i])
		{
			int j=e[i];
			d[j]--;
			if(d[j]==0) q[tt++]=j;
		}
	}
	return tt==n;
}
int main()
{
	cin>>n>>m;
	memset(d,0,sizeof d);
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		add(a,b);
		d[b]++;
	}
	if(!topsort()) cout<<-1;
	else 
	{
		for(int i=0;i<n;i++)
		{
			cout<<q[i]<<" ";
		}	
	}
	return 0;
}
  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值