图的基本算法

              整理了一下图的基本算法,包括图的深度优先遍历、广度优先遍历、拓扑排序、三种单源最短路径(Bell-Ford、Dijkstra、有向无回路最短路径算法)、关键路径。

              图的存储结构采用的邻接表。

              这里贴出算法的关键部分,具体的实现已经上传到我的资源上了。

              ”graphOperation.cpp“

               

//图的操作:广度优先遍历、深度优先遍历、拓扑排序、单源最短路径(Bellman-Ford, DAG, Dijkstra)、关键路径
#include "h1.h"
#include "QueueDefinition.h"
#include "StackDefinition.h"

void createGraph(ALGraph *g){ //为了方便,图的顶点名称就用顶点的序号代替

	int i, j,vexNum, arcNum;
	int va, vb, w;
	ArcNode *arc, *tempArc;

	printf("请输入图的顶点数,边数: ");
	scanf("%d%d", &vexNum, &arcNum);

	g->vexNum = vexNum;
	g->arcNum = arcNum;

	for(i=0; i<6; i++){                 //初始化顶点
		g->vertices[i].data = i;
		g->vertices[i].firstArc = NULL;
	}

	printf("请输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n");
	
	for(j=0; j<arcNum; j++){

		scanf("%d%d%d", &w, &va, &vb);
		arc = (ArcNode*)malloc(sizeof(ArcNode));
		arc->weight = w;
		arc->adjvex = vb;
		arc->nextArc = NULL;

		tempArc = g->vertices[va].firstArc;//插入该弧
		g->vertices[va].firstArc = arc;
		arc->nextArc = tempArc;

		printf("Input next arc:\n");
	
	}
}

Boolean visited[MAXSIZE];

void visitGraphNode(ALGraph g, int v){

	printf(" %d ", g.vertices[v].data);
}

void BFSTraverse(ALGraph g, SqQueue *Q,  int v){//从顶点v开始的广度遍历

	int n = g.vexNum;
	int adjVex;
	VNode *vNode;
	ArcNode  *arcNode;

	
	vNode = (VNode *)malloc(sizeof(VNode));
	if(!vNode){
		printf("Memory allocation error!\n");
		exit(OVERFLOW);
	}
	
	EnQueue(Q, g.vertices[v]);

	while(QueueLength(*Q) != 0){

		DeQueue(Q, vNode);
		
		if(!visited[vNode->data]){
			visitGraphNode(g, vNode->data);
			visited[vNode->data] = TRUE;
			arcNode = vNode->firstArc;
			while(arcNode){
				adjVex = arcNode->adjvex;
				EnQueue(Q, g.vertices[adjVex]);
				arcNode = arcNode->nextArc;
			}
		}
	}
	free(vNode);
	ClearQueue(Q);
}

void graphBFSTraverse(ALGraph g, int v){//以顶点v开始的所有顶点的宽度遍历

	int n = g.vexNum;
	int i;
	SqQueue *Q;

	Q = (SqQueue *)malloc(sizeof(SqQueue));
	if(!Q){
		printf("Q initialization error!\n");
		exit(OVERFLOW);
	}
	InitQueue(Q);

	for(i=0; i<n; i++){
		visited[i] = FALSE;
	}

	for(i=v; i<n; i++){
		if(!visited[i]){
			BFSTraverse(g, Q, i);
		}
	}

	for(i=0; i<v; i++){

		if(!visited[i]){
			BFSTraverse(g, Q, i);
		}
	}

	DestroyQueue(Q);
}

void DFSTraverse(ALGraph g, int v){

	visited[v] = TRUE;
	visitGraphNode(g, v);
	ArcNode *arcNode;

	arcNode = g.vertices[v].firstArc;
	while(arcNode){
		if(!visited[arcNode->adjvex]){
			DFSTraverse(g, arcNode->adjvex);
		}
		arcNode = arcNode->nextArc;
	}
}

void graphDFSTraverse(ALGraph g, int v){

	int i;
	int n = g.vexNum;

	for(i=0; i<n; i++){
		visited[i] = FALSE;
	}

	for(i=v; i<n; i++){
		if(!visited[i]){
			DFSTraverse(g, i);
		}
	}

	for(i=0; i<v; i++){
		if(!visited[i]){
			DFSTraverse(g, i);
		}
	}
}

void FindInDegree(ALGraph g, int inDegree[]){

	int i;
	int n = g.vexNum;
	ArcNode *arcNode;

	for(i=0; i<n; i++){
		inDegree[i] = 0;
	}

	for(i=0; i<n; i++){
		arcNode = g.vertices[i].firstArc;
		while(arcNode){
			inDegree[arcNode->adjvex]++;
			arcNode = arcNode->nextArc;
		}
	}
}

Status TopologicalSort(ALGraph g, SqQueue *q){//拓扑排序,将拓扑排序结果保存在队列中

	int inDegree[MAXSIZE];
	int i, k, n;
	int count = 0;
	SqStack *S;
	ArcNode *arcNode;

	n = g.vexNum;

	S = (SqStack*)malloc(sizeof(SqStack));
	if(!S){
		printf("初始化栈失败!\n");
		exit(OVERFLOW);
	}

	FindInDegree(g, inDegree);
	
	InitStack(S);

	for(i=0; i<n; i++){
		if(inDegree[i] == 0){
			Push(S, i);
		}
	}

	while(!StackEmpty(*S)){
		Pop(S, &i);
		EnQueue(q, g.vertices[i]); 
		count++;
		arcNode = g.vertices[i].firstArc;
		
		while(arcNode){
			k = --inDegree[arcNode->adjvex];
			if(k == 0){
				Push(S, arcNode->adjvex);
			}
			arcNode = arcNode->nextArc;
		}
	}

	if(count<g.vexNum){
		DestroyStack(S);
		return ERROR; //有回路
	}
	else{
		DestroyStack(S);
		return OK;
	}
}

void printPath(int s, int n, int k, int path[][MAXSIZE]){//由于在path中存储的路径是逆序的,为了能正序的打印路径,我们这里使用递归方式打印

	k = path[n][k];
	if(k != s){
		printPath(s, n, k, path);
		printf(" %d ", k);
	}
}

void ShortestPath_BellmanFord(ALGraph g, int s){ //无“负权回路”的带权有向图单源最短路径算法

	int n = g.vexNum;
	int i, j, k, u, v;
	ArcNode *arcNode;

	int d[MAXSIZE];//用来描述从源点s到v的最短路径上权值的上界
	int path[MAXSIZE][MAXSIZE];
	for(i=0; i<n; i++){
		d[i] = INFINITY;
		for(j=0; j<n; j++){
			path[i][j] = s;
		}
	}
	d[s] = 0;
	path[s][s] = s;

	for(i=0; i<n-1; i++){  //松弛技术
		for(u=0; u<n; u++){
			arcNode = g.vertices[u].firstArc;
			while(arcNode){
				v = arcNode->adjvex;
				if(d[v] > d[u]+arcNode->weight){
					d[v] = d[u] + arcNode->weight;

					k = path[u][u];
					path[v][u] = k;
					while(k != s){
						
						path[v][k] = path[u][k];
						k = path[u][k];
					}
					path[v][v] = u;
				}
				arcNode = arcNode->nextArc;
			}
		}
	}
	
	for(i=0; i<n; i++){         //打印最短距离和路径
		if(d[i]==INFINITY){
			printf("%d->%d:不可达\n", s, i);
		}
		else{
			printf("%d->%d: %d   ", s, i, d[i]);
			printf("路径为: %d", s);
			printPath(s, i, i, path);
			printf(" %d \n", i);
		}
	}
} //时间复杂度为O(VE)  V:顶点数  E:边数

Status ShortestPath_DAG(ALGraph g, int s){ //对于有向无回路的单源最短路径,我们可以用在更低的时间复杂度内完成

	int n = g.vexNum;
	int i, j, k, u, v;
	ArcNode *arcNode;
	VNode *vNode;
	SqQueue *q;

	vNode = (VNode*)malloc(sizeof(VNode));
	if(!vNode){
		printf("Allocate memory for vNode error!\n");
		exit(OVERFLOW);
	}

	q = (SqQueue*)malloc(sizeof(SqQueue));
	if(!q){
		printf("Allocate memory for queue error!\n");
		exit(OVERFLOW);
	}
	InitQueue(q);

	if(!TopologicalSort(g, q)){
		printf("ERROR!此图有回路,无法使用此算法求最短路径!\n");
		return ERROR;
	}

	int d[MAXSIZE];//用来描述从源点s到v的最短路径上权值的上界
	int path[MAXSIZE][MAXSIZE];
	for(i=0; i<n; i++){
		d[i] = INFINITY;
		for(j=0; j<n; j++){
			path[i][j] = s;
		}
	}
	d[s] = 0;
	path[s][s] = s;

	while(!QueueEmpty(*q)){  //执行松弛技术;在有向无回路图中,松弛技术只执行了|E|次(即边数的次数,采用的是聚集分析技术)
		DeQueue(q, vNode);
		u = vNode->data;
		arcNode = vNode->firstArc;
		while(arcNode){
			v = arcNode->adjvex;
			if(d[v] > d[u] + arcNode->weight){
				d[v] = d[u] + arcNode->weight;
				k = path[u][u];
				path[v][u] = k;
				while(k != s){
					path[v][k] = path[u][k];
					k = path[u][k];
				}
				path[v][v] = u;
			}
			arcNode = arcNode->nextArc;
		}
	}
	DestroyQueue(q);
	free(vNode);

	for(i=0; i<n; i++){         //打印最短距离和路径
		if(d[i]==INFINITY){
			printf("%d->%d:不可达\n", s, i);
		}
		else{
			printf("%d->%d: %d   ", s, i, d[i]);
			printf("路径为: %d", s);
			printPath(s, i, i, path);
			printf(" %d \n", i);
		}
	}

	return OK;
}//时间复杂度为O(V+E) V:顶点数 E:边数

int minimum(int d[], int n){

	int i, k, min;
	min = INFINITY;
	for(i=0; i<n; i++){
		if(d[i] < min){
			min = d[i];
			k = i;
		}
	}

	return k;
}

void ShortestPath_Dijkstra(ALGraph g, int s){ //带“正值”权的图的单源最短路径

	int n = g.vexNum;
	int i, j, k, u, v;
	Boolean final[MAXSIZE];
	ArcNode *arcNode;

	int d[MAXSIZE];//用来描述从源点s到v的最短路径上权值的上界
	int path[MAXSIZE][MAXSIZE];
	for(i=0; i<n; i++){
		d[i] = INFINITY;
		final[i] = FALSE;
		for(j=0; j<n; j++){
			path[i][j] = s;
		}
	}
	d[s] = 0;
	path[s][s] = s;
	final[s] = TRUE;

	arcNode = g.vertices[s].firstArc;  //找到源点的邻接点
	while(arcNode){
		v = arcNode->adjvex;
		d[v] = arcNode->weight;
		path[v][v] = s;
		arcNode = arcNode->nextArc;
	}

	for(i=0; i<n-1; i++){
		u = minimum(d, n);//找到已经获得最短路径的点(贪心性质)
		final[u] = TRUE;
		arcNode = g.vertices[u].firstArc;
		while(arcNode){  //对该结点的邻接点进行松弛操作
			v = arcNode->adjvex;
			if(d[v] > d[u] + arcNode->weight){
				d[v] = d[u] + arcNode->weight;
				k = path[u][u];
				path[v][u] = k;
				while(k != s){
					path[v][k] = path[u][k];
					k = path[u][k];
				}
				path[v][v] = u;
			}
			arcNode = arcNode->nextArc;
		}
	}

	for(i=0; i<n; i++){         //打印最短距离和路径
		if(d[i]==INFINITY){
			printf("%d->%d:不可达\n", s, i);
		}
		else{
			printf("%d->%d: %d   ", s, i, d[i]);
			printf("路径为: %d", s);
			printPath(s, i, i, path);
			printf(" %d \n", i);
		}
	}
}

Status TopologicalOrder(ALGraph g, SqStack *t, int ve[]){//拓扑排序,并返回逆序,为求关键路径做准备

	int inDegree[MAXSIZE];
	int i, k, n, u, v;
	int count = 0;
	SqStack *S;
	ArcNode *arcNode;

	n = g.vexNum;

	S = (SqStack*)malloc(sizeof(SqStack));
	if(!S){
		printf("初始化栈失败!\n");
		exit(OVERFLOW);
	}

	FindInDegree(g, inDegree);
	
	InitStack(S);

	for(i=0; i<n; i++){
		if(inDegree[i] == 0){
			Push(S, i);
		}
		ve[i] = 0;
	}

	while(!StackEmpty(*S)){
		Pop(S, &u);
		Push(t, u); 
		count++;
		arcNode = g.vertices[u].firstArc;
		
		while(arcNode){
			v = arcNode->adjvex;
			if(--inDegree[v] == 0){
				Push(S, v);
			}
			if(ve[v] < ve[u] + arcNode->weight){  //计算每个顶点的最早开始时间
				ve[v] = ve[u] + arcNode->weight;
			}
			arcNode = arcNode->nextArc;
		}
	}

	if(count<g.vexNum){
		DestroyStack(S);
		return ERROR; //有回路
	}
	else{
		DestroyStack(S);
		return OK;
	}
}

Status CriticalPath(ALGraph g){ //求关键路径,即最长路径

	int i, k, u, v, n;
	ArcNode *arcNode;
	SqStack *t;
	int ve[MAXSIZE];  //顶点的最早开始时间
	int vl[MAXSIZE];  //顶点的最迟开始时间

	n = g.vexNum;

	t = (SqStack*)malloc(sizeof(SqStack));
	if(!t){
		printf("Allocate memory for stack error!\n");
		exit(OVERFLOW);
	}

	InitStack(t);
	
	if(!TopologicalOrder(g, t, ve)){
		printf("ERROR!有回路,无法求得关键路径!\n");
		return ERROR;
	}

	for(i=0; i<n; i++){
		vl[i] = ve[n-1];
	}

	while(!StackEmpty(*t)){
		Pop(t, &u);
		arcNode = g.vertices[u].firstArc;
		while(arcNode){
			v = arcNode->adjvex;
			if(vl[u] > vl[v] - arcNode->weight){
				vl[u] = vl[v] - arcNode->weight;
			}
			arcNode = arcNode->nextArc;
		}
	}

	int ee, el;
	for(u=0; u<n; u++){
		arcNode = g.vertices[u].firstArc;
		while(arcNode){
			v = arcNode->adjvex;
			ee = ve[u];
			el = vl[v]- arcNode->weight;
			if(ee == el){
				printf("%d ==> %d\n", u, v);
			}
			arcNode = arcNode->nextArc;
		}
	}
	DestroyStack(t);

	return OK;
}

int main(){

	ALGraph *g;
	
	g = (ALGraph*)malloc(sizeof(ALGraph));
	if(!g){
		printf("Allocate memory for graph error!\n");
		exit(OVERFLOW);
	}

	createGraph(g);

	printf("\n\n****************\n");
	printf("广度优先遍历结果:\n");
	graphBFSTraverse(*g, 0);

	printf("\n\n****************\n");
	printf("深度优先遍历:\n");
	graphDFSTraverse(*g, 0);

	printf("\n\n***************\n");
	printf("拓扑排序:\n");
	SqQueue *q;
	VNode *vNode;
	vNode = (VNode*)malloc(sizeof(VNode));
	if(!vNode){
		printf("Allocate memory for vNode error!\n");
		exit(OVERFLOW);
	}
	q = (SqQueue*)malloc(sizeof(SqQueue));
	if(!q){
		printf("Allocate memory for queue error!\n");
		exit(OVERFLOW);
	}
	InitQueue(q);
	TopologicalSort(*g, q);
	while(!QueueEmpty(*q)){
		DeQueue(q, vNode);
		visitGraphNode(*g, vNode->data);
	}
	free(vNode);
	DestroyQueue(q);
	
	printf("\n\n****************\n");
	printf("最短路径(Bellman-Ford):\n");
	ShortestPath_BellmanFord(*g, 0);

	printf("\n\n****************\n");
	printf("最短路径(DAG):\n");
	ShortestPath_DAG(*g, 0);

	printf("\n\n****************\n");
	printf("最短路径(Dijkstra):\n");
	ShortestPath_DAG(*g, 0);

	printf("\n\n****************\n");
	printf("关键路径:\n");
	CriticalPath(*g);

	free(g);

	return 1;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值