图算法

本文介绍了图的存储方式,包括邻接矩阵和邻接表。接着讲解了图的遍历方法,如DFS和BFS。详细阐述了Dijkstra算法用于求解单源最短路径,以及Floyd算法用于解决全源最短路径问题。同时,讨论了最小生成树的Prim和Kruskal算法,强调了它们在不同场景下的适用性选择。
摘要由CSDN通过智能技术生成

存储图

可用邻接矩阵和邻接表存储,当图中点个数不超过1000时都可用邻接矩阵


遍历图

  1. dfs
    直接递归
int G[max][max];//邻接矩阵
int n;//顶点
bool vis[max]={false};
void dfs(int a){
	vis[a]=true;
	for(int i=0;i<n;i++){
		if(G[a][i]!=inf&&vis[i]==false){
			dfs(i);
		};
	};
}
void set_dfs(){
	for(int i=0;i<n;i++){
		if(vis[i]==false){
			dfs(i);
		};
	};
}
//确保每个连通块都被访问
  1. bfs
    用辅助队列
int G[max][max];
int n;
int vis[max]={false};//顶点曾入过队列
void bfs(s){
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int t=q.front();
		q.pop();
		for(int i=0;i<n;i++){
			if(G[t][i]!=inf&&vis[i]==false){
				q.push(i);
				vis[i]==true;
			};
		};
	};
}
void set_bfs(){
	for(int i=0;i<n;i++){
		if(vis[i]==false){
			bfs(i);
		};
	};
}//遍历所有连通块

Dijkstra

即起点固定,从起点到其他点的路径记录保持最短。

int n;
int G[max][max];
int vis[max];//检测是否已经遍历过该点
int d[max];//每个点到起点的最短距离
int s;//起点
//1.记录路径int pre[max];
//2.记录边权 int cost[max][max];
//2.每个点到起点最短路径的最优边权和 int c[max];
//3.记录点权 int weight[max];
//3.每个点到起点最短路的最优点权和 int w[max];
//4.每个点到起点最短路的条数 int num[max];
void dijkstra(){
	fill(vis,vis+n,0);
	fill(d,d+n,inf);//初始化
	d[s]=0;
	//2.边权初始化 c[s]=0;
	//3.点权初始化 
	//fill(w,w+n,0);
	//w[s]=weight[s];
	//4.最短路初始化
	//fill(num,num+n,0);
	//num[s]=1;
	for(int i=0;i<n;i++){//遍历n次
		int min;
		int u=-1;
		for(int j=0;j<n;j++){
			if(vis[i]==0&&d[i]<min){
				min=d[i];
				u=i;
			};
		};
		if(u==-1)return;
		vis[u]=1;
		//以u为中介点开始优化
		for(int j=0;j<n;j++){
			if(vis[j]==0&&G[u][j]!=inf&&d[j]<G[u][j}+d[u]){
				d[j]=G[u][j]+d[u];
				//1.pre[j]=u;
				//2.c[j]=c[u]+cost[u][j];
				//3.w[j]=w[u]+weight[j];
				//4.num[j]=num[u];
			};
			//2.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
			//		if(c[j]>c[u]+cost[u][v]){
			//	  		c[j]=c[u]+cost[u][v];
			//		};
			//	};
			//3.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
			//		if(w[j]<w[u]+weight[j]){
			//			w[j]=w[u]+weight[j];
			//		};
			//	};
			//4.if(vis[j]==0&&G[u][j]!=inf&&d[j]==G[u][j]+d[u]){
			//		num[j]+=num[u];
			// };
		};

额外要求:

  1. 记录从s到e(任意指定一终点)的最短路径
    设置记录前驱的数组pre[],令pre[v]表示从s到v最短路上v的前一个节点。

  2. 在路径最短条件下新增边权最少
    将题目输入的边权信息计入cost[u][v]中,并新开数组d记录每个点到起点最短路的最优边权。

  3. 在路径最短条件下新增点权最多
    将题目输入的点权信息计入weight[]中,并新开数组w记录每个点到起点最短路中的最优点权。

  4. 计算最短路径条数
    新增数组num,记从起点到顶点u的最短路径条数为num[u].初始化只有起点的num[s]=1,其余都为0.


floyd

floyd为解决全源最短路问题,即对给定的任意两点得出它们之间的最短路径。
由于算法复杂度限制,当顶点数在200以内时可用邻接矩阵实现floyd。

流程:
** 先枚举中介点k,再枚举所有顶点对i,j。**观察是否以k为中介点能使i,j之间路径长度改善。

int n;//顶点数
int m;//边数
int div[max][max];//顶点ij的最短距离
fill(div[0],div[0]+max*max,inf);//初始化,inf表示不是通路,无需div[i][i]=0,会干扰判断.
void floyd(){
	for(int k=0;k<n;k++){
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				if(dis[k][j]!=inf&&dis[i][k]!=inf&&dis[i][j]<dis[i][k]+dis[k][j]){
					dis[i][j]=dis[i][k]+dis[k][j];
				};
			};
		};
	};
}				

最小生成树

  1. 性质
    连通图中所有点的前提下边权最小。
    最小生成树可能不唯一,但边权之和一定唯一。
    最小生成树在无向图上生成。
  2. prim算法
    对图G设置集合S,存放已被访问的顶点,然后每次从剩下的点集合中选择与集合S最短距离最小的一个顶点,访问该点并加入S。之后以该点为中介点,优化所有该点能达到的点与集合s之间的最短距离。
    执行上述操作直到所有点已被访问。
    代码与dijkstra相似
 int n;//顶点数
 int G[max][max];//存放边权
 int d[max];//顶点与集合s的最短距离
 int vis[max]={0};//标记数组
 //假设0为给出最小生成树根节点
 int prim(){
 	//初始化
 	fill(d,d+max,inf);
 	d[0]=0;
 	int sum=0;//存放最小生成树边权之和
 	for(int i=0;i<n;i++){
 		int min=inf;
 		int u=-1;
 		for(int j=0;j<n;j++){
 			if(vis[j]==0&&d[j]<min){
 				u=j;
 				min=d[j];
 			};
 		};
 		if(u==-1){return -1;};
 		vis[u]=1;
 		sum+=d[u];//将与集合的最小距离的边加入最小生成树
 		for(int v=0;v<n;v++){//以u为中介点更新其余点到集合s的距离
 			if(vis[v]==0&&G[u][v]!=inf&&G[u][v]<d[v]){
 				d[v]=G[u][v];
 			};
 		};
 	};
 	return sum;
 }
  1. kruskal算法
    使用并查集+排序
    先按边权大小从小到大排序。再测试每条边,如果两个顶点不属于同一连通块,则加入最小生成树,否则将边舍弃。
    当所有边测试完或者最小生成树中边数等于顶点数-1时结束程序。
    而当结束时,最小生成树中的边数小于总顶点数-1,则该图不连通。

    判定函数结束条件:遍历完所有边。
    剪枝条件:当前最小生成树边数已满足总顶点数-1。
    判定不连通条件:遍历完边后,当前最小生成树边数不满足总顶点数-1。

struct edge{
	int u,v;//边的两个顶点
	int cost;//边权
}
//边数组
vector<edge>list;
//并查集数组
int f[max];
//并查集
int findf(int a){
	int root=a;
	while(root!=f[root]){
		root=f[root];
	};
	//压缩
	while(f[a]!=root){
		int t=f[a];
		f[a]=root;
		a=t;
	};
	return root;
}
//n为顶点数,m为边数
int krus(int n,int m){
	int sum=0;
	int mm=0;//当前生成树的边数
	for(int i=1;i<=n;i++){
		f[i]=i;
	};//并查集数组初始化
	sort(list.begin(),list.end(),cmp);//按边大小排序
	//遍历所有边
	for(int i=0;i<list.size();i++){
		if(findf(list[i].u)!=findf(list[i].v)){
			f[findf(list[i].u)]=f[findf(list[i].v)];//合并
			sum+=list[i].cost;
			mm++;
			if(mm=n-1){break;};
		};
	};
	if(mm!=n-1)return -1;
	else return sum;
}	

5.边少用kruskal,边多用prim。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值