用栈或队列递归求所有拓扑序列

一、输入一个有向图,计算并输出该有向图所可能拥有的所有的拓扑序列。如果该有向图没有拓扑序列,也要输出“没有拓扑序列”的提示。

img


具体要求如下:
(1)将以上有向图用邻接矩阵表示出来,存放在数组int graph[9][9]之中;(5分)
(2)设立一个整型数组inDegree[],用来存储所有结点的入度,在拓扑排序之前将所有顶点的入度计算出来并存储在该数组中;(5分)
(3)在拓扑排序的过程中,不允许重复扫描inDegree数组以发现入度为0的顶点,而是应该另外开辟一个队列(或堆栈)来存储那些入度为0的顶点序号,并动态修改该队列(或堆栈);(5分)
(4)利用递归算法输出所有可能的拓扑序列,递归函数原型如:void topSort(int graph[][9],int inDegree[],int path[],ZeroSet &zs,int k),其中graph是邻接矩阵,inDegree存放所有顶点的入度,path存放当前找到的拓扑序列,zs用来存放所有当前入度为0的结点的序号,数据类型ZeroSet可自己定义,k是数组path中当前需要填充的元素的下标,当k==9时表示发现了一条完整的拓扑序列,可以将其输出;(12分)
(5)需要输出该有向图的全部215条拓扑序列,例如以如下方式输出;(3分)

img

(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 是有向图的结点数。在实际应用中,由于实际场景的输入规模有限,这个算法能够承受的规模也比较有限,但是适用于本题数据规模。

这个代码实现的思路是:

  1. 将邻接矩阵表示的有向图转换为所有顶点的入度数组,在开始拓扑排序之前做好准备工作;
  2. 用一个 vector 存储当前入度为 0 的点的集合,递归遍历所有可能的拓扑序列;
  3. 对于每个递归调用,计算当前所有入度为 0 的结点,依次尝试每个入度为 0 的结点,对每个尝试情况,依次删除与之相关的所有有向边,并将该结点加入拓扑序列中;
  4. 对每个尝试情况,再次递归调用遍历下一个结点,恢复现场,对之前删除的有向边进行还原,并将该结点从拓扑序列中删除;
  5. 如果当前递归调用序列已经找到了 n 个结点,表示我们已经找到一条完整的拓扑序列,将其加入结果集合中输出。

该算法的空间复杂度主要在于 vector 的使用,为 O(n^2),其中 n 是有向图的结点数。可以通过适当优化实现。