图的常用算法思路及实现(c语言)

目录

图的存储结构

邻接矩阵

邻接表

 十字链表

邻接多重表

图的遍历算法

图的广度优先算法(BFS)

图的深度优先算法(DFS)

图的最小生成树的算法

普里姆算法(Prim)

克鲁斯卡尔算法(Kruskal)

二者对比:

图的最短路径算法

​编辑

从某个源点到其余顶点的最短路径 

每一对顶点之间的最短路径

图的拓扑排序 

拓扑排序定义:

排序思路:

代码阶段:

准备工作:

步骤:

图的关键路径


图的存储结构

邻接矩阵

 代码如下:

#define MaxInt 32767
#define MVNum 10


//图的存储结构
typedef struct {
	char vers[MVNum];
	int arcs[MVNum][MVNum];
	int vexnum, arcnum;
	int visited[MVNum];
}AMGraph;

//图的邻接矩阵
int CreateUDN(AMGraph* G) {
	char v1, v2;
	int w,tmp1,tmp2;
	printf("请输入结点数以及边数\n");
	scanf_s("%d%d",&G->vexnum, &G->arcnum);
	for (int i = 0; i < G->vexnum; i++) {
		printf("请输入第%d个结点\n", i + 1);
		scanf_s("%c\n", &G->vers[i], 1);
	}
	for (int i = 0; i < G->vexnum;i++) {
		for (int j = 0; j < G->vexnum;j++) {
			G->arcs[i][j] = MaxInt;
			printf("邻接矩阵初始化完成\n");
		}
	}
	for (int k = 0; k < G->arcnum;k++) {
		printf("请输入第%d个边(v1 v2 权值)\n",k+1);
		scanf_s("%c%c%d",&v1,1,&v2,1,&w);
		tmp1 = LocateVex(*G, v1);
		tmp2 = LocateVex(*G, v2);
		G->arcs[tmp1][tmp2] = w;
		G->arcs[tmp2][tmp1] = w;
	}
	return 1;
}

优缺点:

邻接表

代码:

//图的存储结构(邻接表)

//边结点
typedef struct ArcNode{
	int adjvex; //该边所指向的顶点的位置
	struct ArcNode* nextarc; //指向下一条边的指针
}ArcNode;

//头节点
typedef struct VNode {
	char data;
	ArcNode* fristarc;
}VNode,AdjList[MVNum];

//邻接表
typedef struct {
	AdjList vertices;
	int vexnum, arcnum; //图的当前顶点数和边数
}ALGraph;

 十字链表

邻接多重表

图一共有四种存储方式,在考研和插本中只考前两种,后两种更好但是更复杂,故而不在此说明

图的遍历算法

图的广度优先算法(BFS)

①访问第一个结点v,然后将visited数组标记为已访问并且将其放入队列中

②然后判断队列是否空,不为空就取出第一个数据,然后将其相邻结点依次访问并放入队列中,且标记已访问。重复②这个步骤直至结束

③以防图出现非连通图,所以需要写一个BFSTraverse方法循环的查看visited数组是否还有未访问的结点,有的话将该节点继续调用BFS,直至全部结点都被访问

邻接矩阵实现

//图的广度优先遍历
void BFS(AMGraph G, SqQueue Q,int v) {
	visit(v);
	G.visited[v] = 1;
	EnQueue(&Q, v);
	while (!QueueEmpty(Q)) {
		DeQueue(&Q, &v);
		for (int w = FristNeighbor(G, v); w >= 0;w = NextNeighbor(G,v,w)) {
			if (!G.visited[w]) {
				visit(w);
				G.visited[w] = 1;
				EnQueue(&Q, w);
			}
		}
	}
}
void BFSTraverse(AMGraph G,SqQueue Q) {
	for (int i = 0; i < G.vexnum; i++) {
		G.visited[i] = 0;
	}
	for (int i = 0; i < G.vexnum;i++) {
		if (!G.visited[i]) {
			BFS(G,Q,i);
		}
	}
}

图的深度优先算法(DFS)

①先访问第一个点

②然后for循环找他的邻接结点,并且将他的结点都调用DFS算法,这样递归调用就会使其一直访问下去,直到它最深的结点然后回溯回去。

③同时也可能出现非完全连通图,所以要循环检查visited数组是否有没有访问到的结点,再次执行一遍。

(1)邻接矩阵实现

//图的深度优先遍历
void DFS(AMGraph G,int v) {
	visit(v);
	G.visited[v] = 1;
	for (int w = FristNeighbor(G, v); w > 0;w = NextNeighbor(G,v,w)) {
		if (!G.visited[w]) {
			DFS(G, w);
		}
	}
}
void DFSTraverse(AMGraph G) {
	for (int i = 0; i < G.vexnum; i++) {
		G.visited[i] = 0;
	}
	for (int i = 0; i < G.vexnum; i++) {
		if (!G.visited[i]) {
			DFS(G,i);
		}
	}

}

图的最小生成树的算法

普里姆算法(Prim)

清晰步骤:

代码: 

void MiniSpanTree_Prim(AMGraph G,char u) {
	int k = LocateVex(G, u);
	char u0,v0;
	//初始化closedge数组
	for (int j = 0; j < G.vexnum;j++) {
		if (j != k) {
			closedge[j].adjvex = u;
			closedge[j].lowcost = G.arcs[k][j];
		}
	}

	//将u"加入"到U中(实际这个U不存在,只是数组中lowcost为0的都默认为加入到U中了)
	closedge[k].lowcost = 0;

	//循环遍历每个结点
	for (int i = 1; i < G.vexnum;i++) {
		//将数组中最小的权值的那个结点下标找到
		k = Min(closedge);

		//将u和k结点打印出去
		u0 = closedge[k].adjvex;
		v0 = G.vers[k];
		printf("%c-%c",u0,v0);

		//将k结点加入U中
		closedge[k].lowcost = 0;

		//循环将closedge数组更新
		for (int j = 0; j < G.vexnum;j++) {
			if (G.arcs[k][j]<closedge[j].lowcost) {
				closedge[j].adjvex = G.vers[k];
				closedge[j].lowcost = G.arcs[k][j];
			}
		}

	}
}

克鲁斯卡尔算法(Kruskal)

准备工作:

步骤:

代码:

//Kruskal算法
struct {
	char Head;
	char Tail;
	int lowcost;
}Edge[arcnum];

int Vexset[MVNum];

void MiniSpanTree_Kruskal(AMGraph G) {
	int v1, v2,vs1,vs2;
	//排序Edge
	sort(Edge);
	//初始化连通分量数组
	for (int i = 0; i < G.vexnum;i++) {
		Vexset[i] = i;
	}
	//循环遍历Edge
	for (int i = 0; i < G.arcnum;i++) {
		v1 = LocateVex(G,Edge[i].Head);
		v2 = LocateVex(G,Edge[i].Tail);
		vs1 = Vexset[v1];
		vs2 = Vexset[v2];

		if (vs1 != vs2) {
			printf("%c-%c",v1,v2);
			for (int i = 0; i < G.vexnum;i++) {
				if (Vexset[i] == vs2) {
					Vexset[i] = vs1;
				}
			}
		}
	}


}

二者对比:

图的最短路径算法

从某个源点到其余顶点的最短路径 

广度优先算法(BFS)

迪杰斯特拉算法(Dijstra)

每一对顶点之间的最短路径

弗洛伊德算法(Floyed)

三种算法比对:

图的拓扑排序 

拓扑排序定义:

排序思路:

从第一个入度为0的结点开始,将其取出,然后将他的结点和边去掉,然后再重复前面的操作就入度为0的结点如此操作,一直下去就可以得到拓扑排序了。

代码阶段:

 这段代码是基于邻接表实现的,左边为邻接表的结构,右边就是代码本身

准备工作:

①indegree数组(当前结点的入度为多少)

②print数组(拓扑排序的结果)

③栈(保存入度为0的结点)

步骤:

①将图、栈、两个数组都初始化好

②遍历indegree数组,将入度为0的结点压入栈中

③当栈不为空的时候,将栈顶元素i出栈并且将其输出到print数组然后count++

④然后for循环邻接表中i指向的结点,将指向结点的入度都减一(indegree减一),并且将入度为0的顶点压入栈中,然后一直重复③。

⑤判断count是否小于顶点数,也就是说print里面是否有全部的顶点输出了,目的是判断成功与否。

逆拓扑排序:

 代码实现:

使用DFS来实现逆拓扑排序

 总结:

图的关键路径

概念:

AOE图:

关键路径和关键活动:

 步骤:

 

 

  

 总结:

 

 

 

 

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值