六、拓扑排序

1.实验目的

(1)掌握图的存储结构及其基本操作,学会定义图的邻接表存储结构,并能在实际中灵活应用;

(2)掌握拓扑排序算法;

(3)通过本实验的具体应用实例,灵活应用拓扑排序并进一步巩固队列/栈的运用。

2.实验内容

用邻接表形式存储以下有向无环图,进行拓扑排序,输出相应拓扑序列。若图中每个顶点都在拓扑序列中,说明图中无环。

 

3.源程序和实验结果

#include <iostream>
#include <string>
using namespace std;
const int STACK_INIT_SIZE = 20;

//----------图的结构声明---------
typedef struct ArcNode
{
    int adjvex;              //该弧所指向的顶点的位置
    struct ArcNode *nextarc; //指向下一条弧的指针
} ArcNode;                   //表结点

typedef struct VNode
{
    string data;       //顶点信息
    ArcNode *firstarc; //指向第一条依附该顶点的弧的指针
} VNode;               //头结点

typedef struct
{
    VNode *AdjList;
    int vexnum, arcnum;
} Graph; //有向图

//--------栈结构的声明--------
typedef struct
{
    int *base;
    int *top;
    int stacksize; //当前已分配的存储空间,以元素为单位
} SqStack;

//建造一个空栈
void InitStack(SqStack &S)
{
    S.base = new int[STACK_INIT_SIZE];
    if (!S.base)
    {
        cout << "存储分配失败" << endl;
        return;
    }
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
} //InitStack

// 插入元素e为新的栈顶元素
void Push(SqStack &S, int e)
{
    *S.top = e;
    S.top++;
} //Push

// 若栈不空,删除S的栈顶元素,用e返回其值
void Pop(SqStack &S, int &e)
{
    S.top--;
    e = *S.top;
} //Pop

//若栈为空,返回true,否则返回false
bool StackEmpty(SqStack S)
{
    if (S.top == S.base)
        return true;
    else
        return false;
}

// 有向图的初始化
void createGraph(Graph &g)
{
    g.vexnum = 12; //顶点数目
    g.arcnum = 16; //边的数目

    g.AdjList = new VNode[g.vexnum];
    //顶点的初始化
    for (int i = 0; i < g.vexnum; i++)
    {
        g.AdjList[i].data = "c" + std::to_string(i + 1);
        g.AdjList[i].firstarc = NULL;
    }
    //边的初始化
    cout << "请输入边的起点和终点所对应的编号(0-11)" << endl;
    for (int i = 0; i < g.arcnum; i++)
    {
        int start, end;
        cin >> start >> end;

        ArcNode *a = new ArcNode;
        a->adjvex = end;
        a->nextarc = NULL;

        a->nextarc = g.AdjList[start].firstarc;
        g.AdjList[start].firstarc = a;
        /*
        为什么我用
        ArcNode a;
        a.adjvex=end;
        a.nextarc=NULL;
        a.nextarc=g.AdjList[start].firstarc;
        g.AdjList[start].first=a;
        就是错的呢?
        */
    }
}

// 输出图g的邻接表
void print(Graph g)
{
    cout << endl
         << "有向图g的邻接表表示如下所示:" << endl;
    for (int i = 0; i < g.vexnum; i++)
    {
        cout << "编号" << i << ": " << g.AdjList[i].data;
        ArcNode *p = g.AdjList[i].firstarc;
        while (p)
        {
            cout << "-->" << p->adjvex;
            p = p->nextarc;
        }
        cout << endl;
    }
}

//求各顶点的入度
void FindInDegree(Graph g, int *indegree)
{
    for (int i = 0; i < g.vexnum; i++)
        indegree[i] = 0;

    for (int i = 0; i < g.vexnum; i++)
    {
        ArcNode *p = g.AdjList[i].firstarc;
        while (p)
        {
            indegree[p->adjvex]++;
            p = p->nextarc;
        }
    }
}

// 拓扑排序
void TopologicalSort(Graph g)
{
    // 有向图g采用邻接表存储结构
    //若g无回路,则输出g的顶点的一个拓扑序列
    int *indegree = new int[g.vexnum];
    FindInDegree(g, indegree);
    //测试
    // for (int i = 0; i < g.vexnum; i++)
    //     cout << indegree[i] << endl;
    SqStack S;
    InitStack(S);
    // 建零入度顶点栈S
    for (int i = 0; i < g.vexnum; i++)
        if (!indegree[i]) //入度为零这进栈
            Push(S, i);
    int count = 0; //对输出顶点计数

    while (!StackEmpty(S))
    {
        int i;
        Pop(S, i);
        //输出i号顶点并计数
        cout << i << " " << g.AdjList[i].data << endl;
        count++;
        for (ArcNode *p = g.AdjList[i].firstarc; p; p = p->nextarc)
        {
            int k = p->adjvex;    //对i号顶点的每个邻接点的入度减1
            if (!(--indegree[k])) //若入度为0,则入栈
                Push(S, k);
        } //for
    }

    if (count < g.vexnum)
    {
        cout << "该有向图有回路" << endl;
        return;
    }
} //TopologicalSort

int main()
{
    Graph g;
    createGraph(g);
    print(g);
    cout << endl
         << "有向图g的拓扑序列为:" << endl;
    TopologicalSort(g);
    return 0;
}

 

测试数据:

0 1
0 2
0 3
0 11
1 2
2 4
2 6
2 7
3 4
4 6
5 7
8 9
8 10
8 11
9 11
10 5

输出结果截图:

4.分析与讨论

posted @ 2021-11-29 23:16  请去看诡秘之主  阅读(29)  评论(0编辑  收藏  举报