一、输入一个有向图,计算并输出该有向图所可能拥有的所有的拓扑序列。如果该有向图没有拓扑序列,也要输出“没有拓扑序列”的提示。
(6)如果有向图没有拓扑序列,要能够发现并给出提示;(3分)
(7)编写任意其他必要的函数,不许将所有代码都写在main()函数中;(6分)
(8)能够正确地申请空间和释放空间;(6分)
(9)算法的时间复杂度低,代码拥有良好的风格。(5分)
以下是我写的:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#define ZeroSet int*
void topSort(int graph[][9], int inDegree[], int path[], ZeroSet &zs, int k);//递归算法输出所有拓扑排序
int top = -1;
int count = 0;
int main()
{
int graph[9][9] = { { 0,0,1,0,0,0,0,1,0 },
{ 0,0,1,1,1,0,0,0,0 },
{ 0,0,0,1,0,0,0,0,0 },
{ 0,0,0,0,0,1,1,0,0 },
{ 0,0,0,0,0,1,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,1 },
{ 0,0,0,0,0,0,1,0,0 } };
int inDegree[9] = { 0 };
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (graph[j][i] != 0)
inDegree[i]++;
}
}
int path[9];
ZeroSet zs = (int *)malloc(9 * sizeof(int));
for (int i = 0; i < 9; i++)//将入度为0的顶点进栈
{
if (inDegree[i] == 0)
{
top++;
zs[top] = i + 1;
}
}
topSort(graph, inDegree, path, zs, 0);
if (count < 1)
printf("没有拓扑序列\n");
system("pause");
return 0;
}
void topSort(int graph[][9], int inDegree[], int path[], ZeroSet &zs, int k)
{
if (k == 9)
{
count++;
printf("%d:", count);
for (int i = 0; i < 9; i++)
{
printf(" C%d,", path[i]);
}
printf("\n");
}
else
{
while (top != -1)
{
int e = zs[top];//出栈
top--;
path[k] = e;//存入路径
for (int i = 0; i < 9; i++)
{
if (graph[e - 1][i] != 0)//邻接点
{
inDegree[i]--;//入度减一
if (inDegree[i] == 0)//入度为0进栈
{
top++;
zs[top] = i + 1;
}
}
}
topSort(graph, inDegree, path, zs, k + 1);
}
}
}
写到这里就不会写了,想问问怎么改啊,后天就考试了
你的代码已经实现了大部分功能,但还需要进行一些修改。
1.在函数topSort中,当栈zs为空时,说明无法继续拓扑排序,应该返回而不是继续递归。可以添加一个条件判断,如果栈为空,直接返回。
2.在函数topSort中,需要在每次递归结束后恢复修改的状态,以便进行下一次的排序。在递归结束后,需要将当前顶点恢复入度为0,并将之前入栈的顶点重新入栈。可以使用一个临时数组来保存之前入栈的顶点,然后在递归结束后重新将这些顶点入栈。
3.在主函数中,需要在输出拓扑序列之前,将栈zs中剩余的顶点依次出栈,并将其入度恢复为0。然后再输出"没有拓扑序列"的提示。
4.在代码的最后,需要释放动态分配的内存,即释放栈zs的内存。
下面是修改后的代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
void topSort(int graph[][9], int inDegree[], int path[], int k, int* count);
void printTopologicalSorts(int graph[][9]);
int main()
{
int graph[9][9] = {
{0, 0, 1, 0, 0, 0, 0, 1, 0},
{0, 0, 1, 1, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 1, 0, 0}
};
printTopologicalSorts(graph);
system("pause");
return 0;
}
void topSort(int graph[][9], int inDegree[], int path[], int k, int* count)
{
if (k == 9)
{
(*count)++;
printf("%d: ", *count);
for (int i = 0; i < 9; i++)
{
printf("C%d ", path[i]);
}
printf("\n");
}
else
{
for (int i = 0; i < 9; i++)
{
if (inDegree[i] == 0)
{
path[k] = i + 1;
inDegree[i] = -1; // 标记已经访问过的节点
for (int j = 0; j < 9; j++)
{
if (graph[i][j] == 1)
{
inDegree[j]--; // 将相邻节点的入度减1
}
}
topSort(graph, inDegree, path, k + 1, count);
// 还原操作
inDegree[i] = 0;
for (int j = 0; j < 9; j++)
{
if (graph[i][j] == 1)
{
inDegree[j]++;
}
}
}
}
}
}
void printTopologicalSorts(int graph[][9])
{
int inDegree[9] = {0};
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (graph[j][i] != 0)
{
inDegree[i]++;
}
}
}
int path[9] = {0};
额可以不要用GPT来水吗
同学,应该符合你的要求了吧?
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 9 // 顶点数的最大值
#define MAX_SEQ_NUM 300 // 最大拓扑序列数
void topSort(int graph[][MAX_VERTEX_NUM], int inDegree[], int path[], int k, int& count, int seq[][MAX_VERTEX_NUM]);
int main()
{
int graph[MAX_VERTEX_NUM][MAX_VERTEX_NUM] = {
{ 0,0,1,0,0,0,0,1,0 },
{ 0,0,1,1,1,0,0,0,0 },
{ 0,0,0,1,0,0,0,0,0 },
{ 0,0,0,0,0,1,1,0,0 },
{ 0,0,0,0,0,1,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,1 },
{ 0,0,0,0,0,0,1,0,0 }
}; // 邻接矩阵表示的有向图
int inDegree[MAX_VERTEX_NUM] = { 0 }; // 记录每个顶点的入度
int path[MAX_VERTEX_NUM] = { 0 }; // 记录拓扑序列
int seq[MAX_SEQ_NUM][MAX_VERTEX_NUM] = { 0 }; // 记录所有可能的拓扑序列
int count = 0; // 记录拓扑序列的个数
// 计算每个顶点的入度
for (int i = 0; i < MAX_VERTEX_NUM; i++)
{
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
if (graph[j][i] != 0)
{
inDegree[i]++;
}
}
}
// 对有向图进行拓扑排序
topSort(graph, inDegree, path, 0, count, seq);
// 输出拓扑序列
if (count > 0)
{
printf("存在%d条拓扑序列:\n", count);
for (int i = 0; i < count; i++)
{
printf("%d: ", i + 1);
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
printf("C%d ", seq[i][j]);
}
printf("\n");
}
}
else
{
printf("不存在拓扑序列\n");
}
system("pause");
return 0;
}
// 递归算法输出所有可能的拓扑序列
void topSort(int graph[][MAX_VERTEX_NUM], int inDegree[], int path[], int k, int& count, int seq[][MAX_VERTEX_NUM])
{
if (k == MAX_VERTEX_NUM) // 找到了一条拓扑序列
{
for (int i = 0; i < MAX_VERTEX_NUM; i++)
{
seq[count][i] = path[i]; // 将拓扑序列存入数组中
}
count++; // 拓扑序列数加 1
return;
}
for (int i = 0; i < MAX_VERTEX_NUM; i++)
{
if (inDegree[i] == 0) // 找到一个入度为 0 的顶点
{
path[k] = i + 1; // 将该顶点加入拓扑序列中
inDegree[i] = -1; // 将该顶点的入度标记为 -1,表示已经处理过
// 将该顶点的所有邻接点的入度减 1
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
if (graph[i][j] != 0)
{
inDegree[j]--;
}
}
// 递归处理下一个顶点
topSort(graph, inDegree, path, k + 1, count, seq);
// 恢复该顶点的入度和拓扑序列
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
if (graph[i][j] != 0)
{
inDegree[j]++;
}
}
inDegree[i] = 0;
path[k] = 0;
}
}
}
// 递归算法输出所有可能的拓扑序列
void topSort(int graph[][MAX_VERTEX_NUM], int inDegree[], int path[], int k, int& count, int seq[][MAX_VERTEX_NUM])
{
if (k == MAX_VERTEX_NUM) // 找到了一条拓扑序列
{
for (int i = 0; i < MAX_VERTEX_NUM; i++)
{
seq[count][i] = path[i]; // 将拓扑序列存入数组中
}
count++; // 拓扑序列数加 1
return;
}
for (int i = 0; i < MAX_VERTEX_NUM; i++)
{
if (inDegree[i] == 0) // 找到一个入度为 0 的顶点
{
path[k] = i + 1; // 将该顶点加入拓扑序列中
inDegree[i] = -1; // 将该顶点的入度标记为 -1,表示已经处理过
// 将该顶点的所有邻接点的入度减 1
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
if (graph[i][j] != 0)
{
inDegree[j]--;
}
}
// 递归处理下一个顶点
topSort(graph, inDegree, path, k + 1, count, seq);
// 恢复该顶点的入度和拓扑序列
for (int j = 0; j < MAX_VERTEX_NUM; j++)
{
if (graph[i][j] != 0)
{
inDegree[j]++;
}
}
inDegree[i] = 0;
path[k] = 0;
}
}
}
#include"stdafx.h"
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1005;
int innode[maxn] = { 0 };//记录一个顶点的入度
vector<int> adj[maxn];//邻接表存储图
int vertexnum, edgenum;
vector<int> path[maxn];//拓扑排序序列
int k = 0;
vector<int> tempath;
bool vis[maxn] = { false };//标记是否被访问
void DFS() {//递归求解拓扑全部排序序列,所有序列保存到path数组中
if (tempath.size() == vertexnum) {//递归边界
for (int i = 0; i < vertexnum; i++) {
path[k].push_back(tempath[i]);
}
k++;
return;
}
for (int u = 1; u <= vertexnum; u++) {
if (innode[u] == 0 && vis[u]==false) {
tempath.push_back(u);//加入到路径中
vis[u] = true;//标记此点已被访问
for (int j = 0; j < adj[u].size(); j++) {
int v = adj[u][j];
innode[v]--;
}
DFS();
//回溯
for (int j = 0; j < adj[u].size(); j++) {
int v = adj[u][j];
innode[v]++;
}
vis[u] = false;
tempath.pop_back();
}
}
}
bool judge(int topo[]) {//判断是否为topo序列的函数
for (int i = 0; i < k; i++) {
int j;
for (j = 0; j < path[i].size(); j++) {
if (topo[j] != path[i][j]) break;
}
if (j == path[i].size()) return true;//如果都匹配上
}
return false;
}
int main() {
cin >> vertexnum >> edgenum;
for (int i = 0; i < edgenum; i++) {
int Start, End;
cin >> Start >> End;
adj[Start].push_back(End);
innode[End]++;
}
DFS();
int m;
cin >> m;
int topo[maxn];
vector<int> recordindex;//记录不符合拓扑排序的序号
for (int i = 0; i < m; i++) {
for (int j = 0; j < vertexnum; j++) {
cin >> topo[j];
}
if (judge(topo) == false) recordindex.push_back(i);
}
for (int i = 0; i < recordindex.size(); i++) {//输出不符号topo排序的序号
cout << recordindex[i];
if (i < recordindex.size()-1) cout << " ";
}
return 0;
}
以下是一个基于邻接矩阵的有向图拓扑排序的代码实现,可以输出该有向图可能拥有的所有的拓扑序列:
#include <iostream>
#include <vector>
using namespace std;
const int N = 9; // 有向图的结点个数
int graph[N][N]; // 邻接矩阵
int inDegree[N]; // 所有结点的入度
int path[N]; // 存储当前找到的拓扑序列
vector<int> candidates; // 当前入度为0的结点集合
void topSort(int k);
void output(vector<int>& path);
int main()
{
// 初始化邻接矩阵
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
graph[i][j] = 0;
}
}
// 添加有向边
graph[0][3] = 1;
graph[0][4] = 1;
graph[1][4] = 1;
graph[2][5] = 1;
graph[3][6] = 1;
graph[4][6] = 1;
graph[5][7] = 1;
graph[6][8] = 1;
graph[7][8] = 1;
// 计算所有结点的入度
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (graph[j][i] != 0) { // 存在一条有向边 (j, i)
inDegree[i]++;
}
}
}
// 开始拓扑排序
topSort(0);
return 0;
}
// k : 当前需要填充的数组下标
void topSort(int k)
{
if (k == N) { // 当前已找到一条完整的拓扑序列
vector<int> tmp(path, path + N);
output(tmp);
return;
}
// 计算当前所有入度为0的结点
for (int i = 0; i < N; i++) {
if (inDegree[i] == 0) {
candidates.push_back(i);
}
}
if (candidates.empty()) { // 没有可用的入度为0的结点,拓扑排序无法继续
cout << "No topological sequence exists." << endl;
return;
}
// 依次尝试所有可用的入度为0的结点
for (int i = 0; i < candidates.size(); i++) {
int node = candidates[i];
path[k] = node; // 在当前下标的位置填充该结点
candidates.erase(candidates.begin() + i); // 从当前入度为0的结点集合中删除该结点
// 依次删除所有以该结点为起点的有向边
for (int j = 0; j < N; j++) {
if (graph[node][j] != 0) {
inDegree[j]--;
}
}
// 继续拓扑排序
topSort(k + 1);
// 恢复状态,用于尝试下一个可用的入度为0的结点
path[k] = -1;
candidates.insert(candidates.begin() + i, node);
for (int j = 0; j < N; j++) {
if (graph[node][j] != 0) {
inDegree[j]++;
}
}
}
candidates.clear();
}
void output(vector<int>& path)
{
for (int i = 0; i < N; i++) {
if (i == N - 1) {
cout << path[i] << endl;
} else {
cout << path[i] << " ";
}
}
}
这个代码其实就是一个经典的回溯算法实现,时间复杂度是 O(n!),其中 n 是有向图的结点数。在实际应用中,由于实际场景的输入规模有限,这个算法能够承受的规模也比较有限,但是适用于本题数据规模。
这个代码实现的思路是:
该算法的空间复杂度主要在于 vector 的使用,为 O(n^2),其中 n 是有向图的结点数。可以通过适当优化实现。