参考王道《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;
}