拓扑结构(王道版)——求拓扑序列(邻接表)、求逆拓扑序列(邻接矩阵)、DFS求拓扑排序与逆拓扑排序(邻接矩阵)

参考王道《2023年数据结构考研复习指导》

一、求拓扑序列(邻接表)

#include <iostream>

#define MaxVerTexNum 20								

typedef int VertexType;				// 顶点的数据类型
typedef int InfoType;				// 带权图中边上权值的数据类型

// 用邻接表存储图
typedef struct ArcNode {			// 边表结点
	int adjvex;						// 该弧所指向的顶点的位置(存储下标)
	InfoType info;					// 边权值
	struct ArcNode* nextarc;		// 指向下一条弧的指针
}ArcNode;

typedef struct VNode {				// 顶点表结点
	VertexType data;				// 顶点信息
	ArcNode* firstarc;				// 指向第一条依附该顶点的弧的指针
}VNode, AdjList[MaxVerTexNum];		

typedef struct {
	AdjList vertices;				// 邻接表
	int vexnum, arcnum;				// 图的顶点数和弧数
} Graph;

// 链栈(带头结点)
typedef struct LiStackNode {
	int data;						// 存储图顶点的下标
	struct LiStackNode* next;
} LiStackNode, * LiStack;

void initGraph(Graph& G);
bool InitStack(LiStack& S);
bool StackEmpty(LiStack S);
bool Push(LiStack& S, int x);
bool Pop(LiStack& S, int& x);
bool GetTop(LiStack S, int& x);
void DestroyStack(LiStack& S);
bool TopologicalSort(Graph G, int print[]);

int main() {
	Graph G;
	initGraph(G);
	int print[MaxVerTexNum];
	if (TopologicalSort(G, print)) {
		for (int i = 0; i < G.vexnum; ++i) {
			std::cout << print[i] << " ";
		}
		std::cout << std::endl;
	}

	system("pause");
	return 0;
}

/*
	第一行输入结点个数和弧的个数,第二行开始输入弧(起点 终点 权值)
	5 5
	0 1 5
	1 3 2
	2 3 7
	2 4 6
	3 4 10
*/
void initGraph(Graph& G) {				// 头插法建立邻接表
	std::cin >> G.vexnum >> G.arcnum;
	
	for (int i = 0; i < G.vexnum; ++i) {
		G.vertices[i].data = i;
		G.vertices[i].firstarc = NULL;
	}

	for (int i = 0; i < G.arcnum; ++i) {
		int u, v, val;
		std::cin >> u >> v >> val;
		ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
		if (p == NULL)
			return; 
		p->adjvex = v;
		p->info = val;
		p->nextarc = G.vertices[u].firstarc;
		G.vertices[u].firstarc = p;
	}
}


/*==================================================================*/
// 初始化链栈(带头结点)
bool InitStack(LiStack& S) {
	S = (LiStackNode*)malloc(sizeof(LiStackNode));
	if (S == NULL)				// 内存分配失败
		return false;

	S->next = NULL;
	return true;
}

// 判断栈空
bool StackEmpty(LiStack S) {
	if (S->next == NULL)
		return true;
	else
		return false;
}

// 进栈操作——新元素入栈
bool Push(LiStack& S, int x) {
	LiStackNode* q = (LiStackNode*)malloc(sizeof(LiStackNode));
	if (q == NULL)				// 内存分配失败
		return false;

	q->data = x;
	q->next = S->next;
	S->next = q;
	return true;
}

// 出栈操作
bool Pop(LiStack& S, int& x) {
	if (S->next == NULL)		// 栈空,报错
		return false;

	LiStackNode* q = S->next;
	x = q->data;
	S->next = q->next;
	free(q);
	return true;
}

// 读栈顶操作
bool GetTop(LiStack S, int& x) {
	if (S->next == NULL)		// 栈空,报错
		return false;

	x = S->next->data;
	return true;
}

// 销毁链栈
void DestroyStack(LiStack& S) {
	LiStackNode* p = S->next;
	LiStackNode* q;
	while (p != NULL) {
		q = p;
		p = p->next;
		free(q);
	}
	free(S);
	S = NULL;
}

/*==================================================================*/
// 求拓扑序列
bool TopologicalSort(Graph G, int print[]) {				
	LiStack S;
	InitStack(S);

	int indegree[MaxVerTexNum] = {0};				// 存储顶点的入度
	for (int i = 0; i < G.vexnum; ++i) {			// 初始化入度数组
		ArcNode* p = G.vertices[i].firstarc;
		while (p) {
			indegree[p->adjvex]++;
			p = p->nextarc;
		}
	}

	for (int i = 0; i < G.vexnum; ++i) {
		if (indegree[i] == 0) {				// 将所有入度为0的顶点进栈
			Push(S, i);
		}
	}

	int count = 0;							// 计数,记录当前已经输出的顶点数
	while (!StackEmpty(S)) {				// 栈不空,则存在入度为0的顶点
		int i;
		Pop(S, i);
		print[count++] = i;
		// 将所有i指向的顶点的入度减一,并且将入度减为0的顶点压入栈S
		for (ArcNode* p = G.vertices[i].firstarc; p != NULL; p = p->nextarc) {
			if (!(--indegree[p->adjvex])) {
				Push(S, p->adjvex);			// 入度为0,则入栈
			}
		}
	}

	DestroyStack(S);

	if (count < G.vexnum)
		return false;						// 排序失败,有向图中有回路
	else
		return true;
}

二、求逆拓扑序列(邻接矩阵)

#include <iostream>

#define MaxVerTexNum 20								

typedef char VertexType;							// 顶点的数据类型
typedef int EdgeType;								// 带权图中边上权值的数据类型

// 邻接矩阵存储图
typedef struct {
	VertexType Vex[MaxVerTexNum];					// 顶点表
	EdgeType Edge[MaxVerTexNum][MaxVerTexNum];		// 邻接矩阵,边表
	int vexnum, arcnum;								// 图的当前顶点数和边数/弧数
} MGraph;

// 链栈(带头结点)
typedef struct LiStackNode {
	int data;						// 存储图顶点的下标
	struct LiStackNode* next;
} LiStackNode, * LiStack;

bool InitStack(LiStack& S);
bool StackEmpty(LiStack S);
bool Push(LiStack& S, int x);
bool Pop(LiStack& S, int& x);
bool GetTop(LiStack S, int& x);
void DestroyStack(LiStack& S);
bool ReverseTopologicalSort(MGraph G, int print[]);

int main() {
	MGraph G = {
		{'0', '1', '2', '3', '4'},
		{
			{-1, 5, -1, -1, -1},
			{-1, -1, -1, 2, -1},
			{-1, -1, -1, 7, 6},
			{-1, -1, -1, -1, 10},
			{-1, -1, -1, -1, -1}
		},
		5, 5
	};

	int print[MaxVerTexNum];
	if (ReverseTopologicalSort(G, print)) {
		for (int i = 0; i < G.vexnum; ++i) {
			std::cout << print[i] << " ";
		}
		std::cout << std::endl;
	}

	system("pause");
	return 0;
}

/*==================================================================*/
// 初始化链栈(带头结点)
bool InitStack(LiStack& S) {
	S = (LiStackNode*)malloc(sizeof(LiStackNode));
	if (S == NULL)				// 内存分配失败
		return false;

	S->next = NULL;
	return true;
}

// 判断栈空
bool StackEmpty(LiStack S) {
	if (S->next == NULL)
		return true;
	else
		return false;
}

// 进栈操作——新元素入栈
bool Push(LiStack& S, int x) {
	LiStackNode* q = (LiStackNode*)malloc(sizeof(LiStackNode));
	if (q == NULL)				// 内存分配失败
		return false;

	q->data = x;
	q->next = S->next;
	S->next = q;
	return true;
}

// 出栈操作
bool Pop(LiStack& S, int& x) {
	if (S->next == NULL)		// 栈空,报错
		return false;

	LiStackNode* q = S->next;
	x = q->data;
	S->next = q->next;
	free(q);
	return true;
}

// 读栈顶操作
bool GetTop(LiStack S, int& x) {
	if (S->next == NULL)		// 栈空,报错
		return false;

	x = S->next->data;
	return true;
}

// 销毁链栈
void DestroyStack(LiStack& S) {
	LiStackNode* p = S->next;
	LiStackNode* q;
	while (p != NULL) {
		q = p;
		p = p->next;
		free(q);
	}
	free(S);
	S = NULL;
}

/*==================================================================*/
// 求逆拓扑序列
bool ReverseTopologicalSort(MGraph G, int print[]) {
	LiStack S;
	InitStack(S);

	int outdegree[MaxVerTexNum] = { 0 };				// 存储顶点的出度
	for (int i = 0; i < G.vexnum; ++i) {
		for (int j = 0; j < G.vexnum; ++j) {
			if (G.Edge[i][j] != -1) {
				outdegree[i]++;
			}
		}
	}

	for (int i = 0; i < G.vexnum; ++i) {
		if (outdegree[i] == 0) {				// 将所有入度为0的顶点进栈
			Push(S, i);
		}
	}

	int count = 0;							// 计数,记录当前已经输出的顶点数
	while (!StackEmpty(S)) {				// 栈不空,则存在入度为0的顶点
		int v;
		Pop(S, v);
		print[count++] = v;
		// 将所有i指向的顶点的入度减一,并且将入度减为0的顶点压入栈S
		for (int u = 0; u < G.vexnum; ++u) {
			if (G.Edge[u][v] != -1) {
				if (!(--outdegree[u])) {
					Push(S, u);
				}
			}
		}
	}

	DestroyStack(S);

	if (count < G.vexnum)
		return false;						// 排序失败,有向图中有回路
	else
		return true;
}

三、DFS求拓扑排序与逆拓扑排序(邻接矩阵)

#include <iostream>

#define MaxVerTexNum 20

typedef char VertexType;							// 顶点的数据类型
typedef int EdgeType;								// 带权图中边上权值的数据类型

// 邻接矩阵存储图
typedef struct {
	VertexType Vex[MaxVerTexNum];					// 顶点表
	EdgeType Edge[MaxVerTexNum][MaxVerTexNum];		// 邻接矩阵,边表
	int vexnum, arcnum;								// 图的当前顶点数和边数/弧数
} MGraph;

bool visited[MaxVerTexNum];
int FirstNeighbor(MGraph G, int x);
int NextNeighbor(MGraph G, int x, int y);
bool DFSTraverse_ReverseTopologicalSort(MGraph G, int print[]);
bool DFS_ReverseTopologicalSort(MGraph G, int v, bool* if_visited, int print[], int& count);
bool DFS_TopologicalSort(MGraph G, int print[]);
bool DFSTraverse(MGraph G, int finishedTime[]);
bool DFS(MGraph G, int v, bool* if_visited, int finishedTime[], int& time);

int main() {
	MGraph G = {			// 无环
		{'0', '1', '2', '3', '4'},
		{
			{-1, 5, -1, -1, -1},
			{-1, -1, -1, 2, -1},
			{-1, -1, -1, 7, 6},
			{-1, -1, -1, -1, 10},
			{-1, -1, -1, -1, -1}
		},
		5, 5
	};

	int print[MaxVerTexNum] = { 0 };
	if (DFSTraverse_ReverseTopologicalSort(G, print)) {
		for (int i = 0; i < G.vexnum; ++i) {
			std::cout << print[i] << " ";
		}
		std::cout << std::endl;
	}

	int print1[MaxVerTexNum] = { 0 };
	if (DFS_TopologicalSort(G, print1)) {
		for (int i = 0; i < G.vexnum; ++i) {
			std::cout << print1[i] << " ";
		}
		std::cout << std::endl;
	}

	MGraph G2 = {			// 有环
		{'0', '1', '2', '3', '4'},
		{
			{-1, 5, -1, -1, -1},
			{-1, -1, -1, 2, -1},
			{-1, -1, -1, 7, 6},
			{-1, -1, -1, -1, 10},
			{-1, -1, 5, -1, -1}
		},
		5, 5
	};

	int print2[MaxVerTexNum] = { 0 };
	if (DFSTraverse_ReverseTopologicalSort(G2, print)) {
		for (int i = 0; i < G2.vexnum; ++i) {
			std::cout << print2[i] << " ";
		}
		std::cout << std::endl;
	}

	int print3[MaxVerTexNum] = { 0 };
	if (DFS_TopologicalSort(G2, print1)) {
		for (int i = 0; i < G2.vexnum; ++i) {
			std::cout << print3[i] << " ";
		}
		std::cout << std::endl;
	}

	system("pause");
	return 0;
}

/*==================================================================*/
// 求图G中顶点x(下标)的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1
int FirstNeighbor(MGraph G, int x) {
	if (x >= G.vexnum)
		return -1;

	int i = 0;
	while (i < G.vexnum) {
		if (G.Edge[x][i] != -1)
			return i;
		i++;
	}
	return -1;
}

// 假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1
int NextNeighbor(MGraph G, int x, int y) {
	if (x >= G.vexnum || y >= G.vexnum)
		return -1;

	int i = y + 1;
	while (i < G.vexnum) {
		if (G.Edge[x][i] != -1)
			return i;
		i++;
	}
	return -1;
}

/*==================================================================*/
// DFS实现逆拓扑排序
bool DFSTraverse_ReverseTopologicalSort(MGraph G, int print[]) {
	for (int i = 0; i < G.vexnum; ++i) {
		visited[i] = false;						// 初始化已访问标记数据
	}

	int count = 0;
	for (int v = 0; v < G.vexnum; ++v) {
		if (!visited[v]) {
			bool if_visited[MaxVerTexNum];		// 判断一次DFS遍历中是否有环
			for (int i = 0; i < G.vexnum; ++i) {
				if_visited[i] = false;
			}
			if (!DFS_ReverseTopologicalSort(G, v, if_visited, print, count))
				return false;
		}
	}

	return true;
}

// 从顶点v出发,深度优先遍历图G
bool DFS_ReverseTopologicalSort(MGraph G, int v, bool* if_visited, int print[], int& count) {		
	visited[v] = true;							// 设置已访问标志
	if_visited[v] = true;						// 设置该顶点在此次DFS遍历中已访问,若在此次DFS遍历中,又访问到该点,说明有环

	for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) {
		if (if_visited[w]) {					// 当该结点已在此次遍历中访问过,则说明有环
			return false;
		}
		if (!visited[w]) {						// w为v的尚未访问的邻接点
			if (!DFS_ReverseTopologicalSort(G, w, if_visited, print, count)) {
				return false;
			}
		}
	}

	print[count++] = v;

	return true;
}
/*==================================================================*/
// DFS实现拓扑排序
// 实质就是逆拓扑排序,再将逆拓扑排序反过来
bool DFS_TopologicalSort(MGraph G, int print[]) {
	int finishedTime[MaxVerTexNum] = { 0 };		// 记录结束时间(按结束时间从大到小,可以得到一个拓扑序列)
	if (!DFSTraverse(G, finishedTime))
		return false;
	
	int top = 0;
	for (int i = 0; i < G.vexnum; ++i) {		// 按结束时间从大到小,得到一个拓扑序列
		int max = -1;
		int k = -1;
		for (int j = 0; j < G.vexnum; ++j) {
			if (max == -1 && finishedTime[j] != -1) {
				max = finishedTime[j];
				k = j;
			}
			else if (max != -1 && finishedTime[j] != -1 && max < finishedTime[j]) {
				max = finishedTime[j];
				k = j;
			}
		}
		finishedTime[k] = -1;					// 得到结束时间最大的结点,并将结束时间设置为-1
		print[top++] = k;						// 加入拓扑序列
	}

	return true;
}

// DFS实现拓扑排序
bool DFSTraverse(MGraph G, int finishedTime[]) {
	for (int i = 0; i < G.vexnum; ++i) {
		visited[i] = false;						// 初始化已访问标记数据
	}

	int time = 0;								// 初始完成时间为0
	for (int v = 0; v < G.vexnum; ++v) {
		if (!visited[v]) {
			bool if_visited[MaxVerTexNum];		// 判断一次DFS遍历中是否有环
			for (int i = 0; i < G.vexnum; ++i) {
				if_visited[i] = false;
			}
			if (!DFS(G, v, if_visited, finishedTime, time))
				return false;
		}
	}

	return true;
}

// 从顶点v出发,深度优先遍历图G
bool DFS(MGraph G, int v, bool* if_visited, int finishedTime[], int& time) {
	visited[v] = true;							// 设置已访问标志
	if_visited[v] = true;						// 设置该顶点在此次DFS遍历中已访问,若在此次DFS遍历中,又访问到该点,说明有环

	for (int w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) {
		if (if_visited[w]) {					// 当该结点已在此次遍历中访问过,则说明有环
			return false;
		}
		if (!visited[w]) {						// w为v的尚未访问的邻接点
			if (!DFS(G, w, if_visited, finishedTime, time)) {
				return false;
			}
		}
	}

	time = time + 1;							// 完成时间+1
	finishedTime[v] = time;						// 记录结点的完成时间
	return true;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈阿土i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值