《数据结构(C语言版)第二版》第六章-图(6.6 图的应用——6.6.3 拓扑排序、 6.6.4 关键路径【有向无环图DAG图】;6.7 案例分析与实现)

6.6 图的应用

拓扑排序 的对象是 :有向无环图 (DAG图)中的 AOV-网,带不带权均可;
关键路径 的对象 :(带权)有向无环网,属于有向无环图 (DAG图)中的 AOE-网,一定带权。

6.6.3 拓扑排序

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100
#define OK 1
#define ERROR -1

typedef char VerTexType;
typedef int OtherInfo;
typedef int Status;

typedef struct ArcNode
{
	int adjvex;
	OtherInfo info;
	struct ArcNode* nextarc;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode,AdjList[MVNum];  //表头结点

typedef struct 
{
	AdjList vertices;  //变ex为ices
	int vexnum;
	int arcnum;
}ALGraph;

typedef struct stackNode
{
	int data; //存入度为0的顶点的下标 
	struct stackNode* next;
}stackNode,*LinkStack;

int indegree[MVNum];  //存放各顶点的入度数量

void CreateDG(ALGraph& G);
int LocateVex(ALGraph G, VerTexType e);
void printALGraph(ALGraph G);
void InitStack(LinkStack &S);
void Push(LinkStack &S, int e);
void Pop(LinkStack &S, int& e);
int EmptyStack(LinkStack S);
void FindinDegree(ALGraph G, int indegree[]);
Status TopologicalSort(ALGraph G, int topo[]);

int main()
{
	ALGraph G = { {'\0',NULL},0,0 };
	int topo[MVNum] = { 0 };

	CreateDG(G);
	printALGraph(G);

	TopologicalSort(G,topo);

	return 0;
}

//创建有向图的邻接表
void CreateDG(ALGraph& G)
{
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcNode* p = NULL;

	printf("请输入总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; i++)
	{
		printf("请输入第%d顶点的值:", i + 1);
		scanf_s(" %c", &G.vertices[i].data);

		G.vertices[i].firstarc = NULL;
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边的两个顶点:",k+1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = j;
		p->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p;
	}
}

int LocateVex(ALGraph G, VerTexType e)
{
	int i = 0;
	for (i = 0; i < G.vexnum && G.vertices[i].data != e; i++)
	{
		;
	}

	return i;
}

void printALGraph(ALGraph G)
{
	int i = 0;
	ArcNode* p = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1,G.vertices[i].data);
		
		p = G.vertices[i].firstarc;
		if (!p)
		{
			printf("\n没有以该顶点为出度的弧。");
			continue;
		}

		printf("\n以该顶点为出度,其所有邻接点为:");
		while (p)
		{
			printf("%c ", G.vertices[p->adjvex].data);;
			p = p->nextarc;
		}
	}
}


//存入各顶点的入度弧数量
void FindinDegree(ALGraph G, int indegree[])
{
	int i = 0;
	ArcNode* p = NULL;
	int num = 0;
	int m = 0; //indegree数组的下标

	for (m = 0; m < G.vexnum; m++)
	{
		for (i = 0; i < G.vexnum; i++)
		{
			p = G.vertices[i].firstarc;

			while (p)
			{
				if (p->adjvex == m)
				{
					num++;
				}

				p = p->nextarc;
			}
		}

		indegree[m] = num;
		num = 0;
	}

	printf("\n\nindegree数组初始元素为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%d ", indegree[i]);
	}
}


//初始化栈
void InitStack(LinkStack &S)
{
	S = NULL;
}

//入栈
void Push(LinkStack &S, int e)
{
	struct stackNode* p = (struct stackNode*)malloc(sizeof(struct stackNode));
	if (!p)
	{
		printf("元素入栈时,内存分配失败。\n");
		return;
	}
	
	p->data = e;
	p->next = S;
	S = p;
}


//出栈
void Pop(LinkStack &S, int& e)
{
	if (!S)
	{
		printf("元素出栈时,栈为空。\n");
		return;
	}

	struct stackNode* p = S;
	e = p->data;
	S = p->next;
	free(p);
	p = NULL;
}


//判断栈空
int EmptyStack(LinkStack S)
{
	if (!S)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


//若 G 无回路,则生成 G 的一个拓扑序列 topo[] 并返回 OK, 否则返回 ERROR
Status TopologicalSort(ALGraph G, int topo[])
{
	int i = 0;
	int j = 0;
	int m = 0;  //对输出顶点计数
	int k = 0;
	ArcNode* p = NULL;

	FindinDegree(G, indegree); //indegree数组中存入各顶点的入度弧数量
	
	LinkStack S = NULL;
	InitStack(S);
	
	for (i = 0; i < G.vexnum; ++i)
	{
		if (!indegree[i])
		{
			Push(S, i);
		}
	}

	while (!EmptyStack(S))
	{
		Pop(S, i);
		topo[m] = i;
		++m;

		p = G.vertices[i].firstarc;

		while (p != NULL)
		{
			k = p->adjvex;
			--indegree[k];

			if (!indegree[k])
			{
				Push(S, k);
			}

			p = p->nextarc;
		}
	}

	if (m < G.vexnum)
	{
		printf("\n\n邻接表中存在回路,生成拓扑序列失败。");
		return ERROR;
	}
	else
	{
		printf("\n\n该邻接表的拓扑序列为:");
		for (i = 0; i < G.vexnum; i++)
		{
			printf("%c ", G.vertices[topo[i]].data);
		}

		return OK;
	}
}

在这里插入图片描述
在这里插入图片描述

6.6.4 关键路径

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100
#define OK 1
#define ERROR -1

typedef char VerTexType;
typedef int ArcType;
typedef int Status;

typedef struct ArcNode
{
	int adjvex;
	ArcType weight;
	struct ArcNode* nextarc;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode, AdjList[MVNum];  //表头结点

typedef struct
{
	AdjList vertices;  //变ex为ices
	int vexnum;
	int arcnum;
}ALGraph;

typedef struct stackNode
{
	int data; //存入度为0的顶点的下标 
	struct stackNode* next;
}stackNode, * LinkStack;

int indegree[MVNum];  //拓扑排序,存放各顶点的入度数量
int ve[MVNum];  //事件Vi的最早发生时间
int vl[MVNum];  //事件Vi的最迟发生时间
int topo[MVNum];  //记录拓扑序列的顶点序号

void CreateDG(ALGraph& G);
int LocateVex(ALGraph G, VerTexType e);
void printALGraph(ALGraph G);
void InitStack(LinkStack& S);
void Push(LinkStack& S, int e);
void Pop(LinkStack& S, int& e);
int EmptyStack(LinkStack S);
void FindinDegree(ALGraph G, int indegree[]);
Status TopologicalSort(ALGraph G);
Status CriticalPath(ALGraph G);


int main()
{
	ALGraph G = { {'\0',NULL},0,0 };
	int topo[MVNum] = { 0 };

	CreateDG(G);
	printALGraph(G);

	CriticalPath(G);

	return 0;
}

//创建有向图的邻接表
void CreateDG(ALGraph& G)
{
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcNode* p = NULL;
	ArcType w = 0;

	printf("请输入总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; i++)
	{
		printf("请输入第%d顶点的值:", i + 1);
		scanf_s(" %c", &G.vertices[i].data);

		G.vertices[i].firstarc = NULL;
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边的两个顶点:", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		printf("请输入第%d条边的权值:", k + 1);
		scanf_s(" %d", &w);

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = j;
		p->weight = w;
		p->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p;
	}
}

int LocateVex(ALGraph G, VerTexType e)
{
	int i = 0;
	for (i = 0; i < G.vexnum && G.vertices[i].data != e; i++)
	{
		;
	}

	return i;
}

void printALGraph(ALGraph G)
{
	int i = 0;
	ArcNode* p = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1, G.vertices[i].data);

		p = G.vertices[i].firstarc;
		if (!p)
		{
			printf("\n没有以该顶点为出度的弧。");
			continue;
		}

		printf("\n以该顶点为出度,其所有邻接点及权值为:");
		while (p)
		{
			printf("%c【%d】 ", G.vertices[p->adjvex].data,p->weight);
			p = p->nextarc;
		}
	}
}


//存入各顶点的入度弧数量
void FindinDegree(ALGraph G, int indegree[])
{
	int i = 0;
	ArcNode* p = NULL;
	int num = 0;
	int m = 0; //indegree数组的下标

	for (m = 0; m < G.vexnum; m++)
	{
		for (i = 0; i < G.vexnum; i++)
		{
			p = G.vertices[i].firstarc;

			while (p)
			{
				if (p->adjvex == m)
				{
					num++;
				}

				p = p->nextarc;
			}
		}

		indegree[m] = num;
		num = 0;
	}

	printf("\n\nindegree数组初始元素为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%d ", indegree[i]);
	}
}


//初始化栈
void InitStack(LinkStack& S)
{
	S = NULL;
}

//入栈
void Push(LinkStack& S, int e)
{
	struct stackNode* p = (struct stackNode*)malloc(sizeof(struct stackNode));
	if (!p)
	{
		printf("元素入栈时,内存分配失败。\n");
		return;
	}

	p->data = e;
	p->next = S;
	S = p;
}


//出栈
void Pop(LinkStack& S, int& e)
{
	if (!S)
	{
		printf("元素出栈时,栈为空。\n");
		return;
	}

	struct stackNode* p = S;
	e = p->data;
	S = p->next;
	free(p);
	p = NULL;
}


//判断栈空
int EmptyStack(LinkStack S)
{
	if (!S)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


//若 G 无回路,则生成 G 的一个拓扑序列 topo[] 并返回 OK, 否则返回 ERROR
Status TopologicalSort(ALGraph G)
{
	int i = 0;
	int j = 0;
	int m = 0;  //对输出顶点计数
	int k = 0;
	ArcNode* p = NULL;

	FindinDegree(G, indegree); //indegree数组中存入各顶点的入度弧数量

	LinkStack S = NULL;
	InitStack(S);

	for (i = 0; i < G.vexnum; ++i)
	{
		if (!indegree[i])
		{
			Push(S, i);
		}
	}

	while (!EmptyStack(S))
	{
		Pop(S, i);
		topo[m] = i;
		++m;

		p = G.vertices[i].firstarc;

		while (p != NULL)
		{
			k = p->adjvex;
			--indegree[k];

			if (!indegree[k])
			{
				Push(S, k);
			}

			p = p->nextarc;
		}
	}

	if (m < G.vexnum)
	{
		printf("\n\n邻接表中存在回路,生成拓扑序列失败。");
		return ERROR;
	}
	else
	{
		printf("\n\n该邻接表的拓扑序列为:");
		for (i = 0; i < G.vexnum; i++)
		{
			printf("%c ", G.vertices[topo[i]].data);
		}

		return OK;
	}
}

//G为邻接表存储的有向网,输出G的各项关键活动
Status CriticalPath(ALGraph G)
{
	if (!TopologicalSort(G))
	{
		printf("拓扑排序算法调用失败。\n");
		return ERROR;
	}

	int n = G.vexnum;
	int i = 0;
	int j = 0;
	int k = 0;
	ArcNode* p = NULL;
	int e = 0;
	ArcType l = 0;

	//给每个事件的最早发生时间置初值0
	for (i = 0; i < n; i++)
	{
		ve[i] = 0;
	}

	//按拓扑次序求每个事件的最早发生时间ve
	for (i = 0; i < n; i++)
	{
		k = topo[i];
		p = G.vertices[k].firstarc;
		while (p)
		{
			j = p->adjvex;
			if (ve[j] < ve[k] + p->weight)
			{
				ve[j] = ve[k] + p->weight;
			}
			p = p->nextarc;
		}
	}



	//给每个事件的最迟发生时间置初值ve[n-1]
	for (i = 0; i < n; i++)
	{
		vl[i] = ve[n - 1];
	}

	//按逆拓扑次序求每个事件的最迟发生时间vl
	for (i = n - 1; i >= 0; i--)
	{
		k = topo[i];
		p = G.vertices[k].firstarc;
		while (p)
		{
			j = p->adjvex;
			if (vl[k] > vl[j] - p->weight)
			{
				vl[k] = vl[j] - p->weight;
			}
			p = p->nextarc;
		}
	}

	printf("\n\n该AOE-网的关键活动是:");

	//判断每一活动是否为关键活动
	for (i = 0; i < n; i++)
	{
		p = G.vertices[i].firstarc;
		while (p)
		{
			j = p->adjvex;
			e = ve[i];
			l = vl[j] - p->weight;
			if (e == l)
			{
				printf(" < %c , %c > ", G.vertices[i].data, G.vertices[j].data);
			}

			p = p->nextarc;
		}
	}

	return OK;
}

在这里插入图片描述
在这里插入图片描述

6.7 案例分析与实现

#include <stdio.h>
#include <stdlib.h>

#define MAX_VERTEX_NUM  20

bool visited[MAX_VERTEX_NUM];
//标记某个顶点是否被访问过。数组中初值均为 "false",如果被访问过,则置其相应的分量为 "true"

typedef enum { Eunvisited, Evisited } EVisitIf;  //标记某条边edge是否被搜索过
typedef int InfoType;   //假设在边结点中的数据域存储边的权值,且边的权值类型为整型
typedef char VerTexType;  //假设顶点的数据类型为字符型

typedef struct EBox
{
	EVisitIf mark;   //mark为标志域,可用以标记该条边是否被搜索过
	int ivex;
	int jvex;
	struct EBox* ilink;
	struct EBox* jlink;
	InfoType* info;
}EBox;

typedef struct VexBox
{
	VerTexType data;
	EBox* firstedge;
}VexBox;

typedef struct
{
	VexBox adjmulist[MAX_VERTEX_NUM];  //表头向量
	int vexnum;  //顶点总数
	int edgenum;  //总边数
}AMLGraph;  //图的信息


typedef struct QNode
{
	int data; //因为每个顶点后面还跟着边的链表,因此队列中只保存顶点在G.adjmulist中的位置数
	struct QNode* next;
}QNode, * QNodeptr;

typedef struct
{
	QNodeptr front;
	QNodeptr rear;
}LinkQueue;  //链队


void CreateAMLGraph(AMLGraph& G);
int LocateVex(AMLGraph& G, VerTexType v);
void printAMLGraph(AMLGraph& G);
void InitQueue(LinkQueue& Q);
void EnQueue(LinkQueue& Q, int e);
int DeQueue(LinkQueue& Q);
int EmptyQueue(LinkQueue& Q);
void SixDegree_BFS(AMLGraph G, int Start);
int FirstAdjVex(AMLGraph G, int i);
int NextAdjVex(AMLGraph G, int i, int w);

int main()
{
	AMLGraph G = { {'\0',NULL,NULL}, 0,0 };
	int i = 0;
	int j = 0;

	CreateAMLGraph(G);
	printAMLGraph(G);


	for (i = 0; i < G.vexnum; i++)
	{
		SixDegree_BFS(G, i+1);

		//将visited数组初始化(重置visited数组)
		for (j = 0; j < G.vexnum; j++)
		{
			visited[j] = false;
		}
	}

	return 0;
}

//采用邻接多重表表示法创建无向图
void CreateAMLGraph(AMLGraph& G)
{
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = 0;
	VerTexType v2 = 0;
	EBox* p1 = NULL;

	printf("请输入人数(无向图的总顶点数):");
	scanf_s(" %d", &G.vexnum);

	printf("请输入两个人“认识”的关系数(无向图的总边数):");
	scanf_s(" %d", &G.edgenum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个人的标识(第%d个顶点的值):", i + 1,i+1);
		scanf_s(" %c", &(G.adjmulist[i].data));
		G.adjmulist[i].firstedge = NULL;
	}

	for (k = 0; k < G.edgenum; k++)
	{
		printf("请输入第%d对认识的两个人(第%d条边依附的两个顶点:) ", k + 1, k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p1 = (EBox*)malloc(sizeof(EBox));

		p1->ivex = i;
		p1->jvex = j;

		p1->ilink = G.adjmulist[i].firstedge;
		G.adjmulist[i].firstedge = p1;

		p1->jlink = G.adjmulist[j].firstedge;
		G.adjmulist[j].firstedge = p1;
	}
}


//在G的顶点表adjmulist中获取字符v的下标(数组G.xlist的下标从0开始)
int LocateVex(AMLGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.adjmulist[i].data != v); ++i)
	{
		;
	}

	return i;
}


//打印邻接多重表
void printAMLGraph(AMLGraph& G)
{
	int i = 0;
	EBox* pMove = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个人(第%d个顶点)为:%c", i + 1, i + 1, G.adjmulist[i].data);
		pMove = G.adjmulist[i].firstedge;

		if (pMove)
		{
			printf("\n与第%d个人认识的人(与第%d个顶点相邻接的顶点)有:", i + 1, i + 1);
			while (pMove)
			{
				if (pMove->ivex == i)
				{
					printf("%c ", G.adjmulist[pMove->jvex].data);
					pMove = pMove->ilink;
				}
				else if (pMove->jvex == i)
				{
					printf("%c ", G.adjmulist[pMove->ivex].data);
					pMove = pMove->jlink;
				}
			}
		}
		else
		{
			printf("\n没有与第%d个人认识的人(没有与第%d个顶点相邻接的顶点)。", i + 1, i + 1);
		}
	}
}


//初始化链队
void InitQueue(LinkQueue& Q)
{
	Q.front = (QNodeptr)malloc(sizeof(QNode));
	if (!Q.front)
	{
		printf("初始化链队时,内存分配失败。\n");
		return;
	}

	Q.rear = Q.front;
	Q.front->next = NULL;
}

//入队
void EnQueue(LinkQueue& Q, int e)
{
	QNodeptr p = (QNodeptr)malloc(sizeof(QNode));
	if (!p)
	{
		printf("元素入队时,新结点内存分配失败。\n");
		return;
	}

	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
}


//出队
int DeQueue(LinkQueue& Q)
{
	if (EmptyQueue(Q))
	{
		printf("元素出队时,链队为空。\n");
		return 0;
	}

	QNodeptr p = Q.front->next;
	int e = p->data;

	Q.front->next = p->next;
	if (p == Q.rear)
	{
		Q.rear = Q.front;
	}

	free(p);
	p = NULL;
	return e;
}


//判空
int EmptyQueue(LinkQueue& Q)
{
	if (Q.front == Q.rear)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


//从第Start个顶点出发,通过广度优先搜索方法遍历G来验证六度空间理论
void SixDegree_BFS(AMLGraph G, int Start)
{
	int Visit_Num = 0;
	LinkQueue Q = { NULL,NULL };
	int len = 1;
	int u = 0;
	int w = 0;

	visited[Start-1] = true;
	InitQueue(Q);
	EnQueue(Q, Start);

	for (len = 1;len<=7 && !EmptyQueue(Q);len++)
	{
		u = DeQueue(Q);  //第一次while循环中:u是队头,即e

		for (w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G, u, w + 1))
		{
			if (!visited[w])
			{
				visited[w] = true;
				Visit_Num++;
				EnQueue(Q, w + 1);
			}
		}
	}


	//printf("\n从第%d个顶点%c出发,与其之间路径长度不超过7的顶点数量为%d,占其余所有顶点数量%d的百分比为:%.2f%% ", Start, G.adjmulist[Start-1].data, Visit_Num, G.vexnum-1, (double)Visit_Num * 100 / (double)(G.vexnum-1));
	printf("\n经历不超过六个人,第%d个人%c可认识的总人数为%d,占该人际关系网络中其余总人数%d 的 %.2f%% ", Start, G.adjmulist[Start - 1].data, Visit_Num, G.vexnum-1, (double)Visit_Num * 100 / (double)(G.vexnum-1));
}


//查找第i个顶点的第一个邻接点下标
int FirstAdjVex(AMLGraph G, int i)
{
	EBox* p = G.adjmulist[i - 1].firstedge;

	if (p)
	{
		if (i - 1 == p->ivex)
		{
			return p->jvex;
		}
		else if (i - 1 == p->jvex)
		{
			return p->ivex;
		}
		else
		{
			return -1;
		}
	}
	else
	{
		return -1;
	}
}

//查找第i个顶点相对于 第w个顶点 邻接点的下一个邻接点的下标
int NextAdjVex(AMLGraph G, int i, int w)
{
	EBox* p = G.adjmulist[i - 1].firstedge;

	// 寻找当前边
	while (p)
	{
		if ((i - 1 == p->ivex && w - 1 == p->jvex) || (i - 1 == p->jvex && w - 1 == p->ivex))
			break;
		p = (i - 1 == p->ivex) ? p->ilink : p->jlink;

		//注意 if 及 p向后移动 的命令顺序

	}

	if (!p)
		return -1; // 如果没有找到当前边则返回 -1

	// 移动到下一个边
	p = (i - 1 == p->ivex) ? p->ilink : p->jlink;

	// 查找下一个邻接顶点
	if (p)
	{
		if (i - 1 == p->ivex)
			return p->jvex;
		else if (i - 1 == p->jvex)
			return p->ivex;
	}

	return -1; // 如果没有找到下一个邻接顶点则返回 -1
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值