实验内容:拓扑排序

实验内容:拓扑排序

任意给定一个有向图,设计一个算法,对它进行拓扑排序。拓扑排序算法思想:a.在有向图中任选一个没有前趋的顶点输出;b.从图中删除该顶点和所有以它为尾的弧;c.重复上述a、b,直到全部顶点都已输出,此时,顶点输出序列即为一个拓朴有序序列;或者直到图中没有无前趋的顶点为止,此情形表明有向图中存在环。

实验说明:

拓扑排序算法伪代码如下:

1. 栈S初始化;累加器count初始化;
2. 扫描顶点表,将没有前驱(即入度为0)的顶点压栈;
3. 当栈S非空时循环
    3.1 vj=退出栈顶元素;输出vj;累加器加1;
3.2 将顶点vj的各个邻接点的入度减1;
3.3 将新的入度为0的顶点入栈;
4. if (count<vertexNum) 输出有回路信息;
  • 的存储结构与基本运算方法

#include<iostream>

#include<iomanip>

using namespace std;

#define MAXV 100//最大结点个数

#define INF 32767//表示不相连

typedef struct ANode {//图的存储结构与基本运算方法

int adjvex;

struct ANode* nextarc;

int weight;

}ArcNode;

typedef struct Vnode {

ArcNode* firstarc;

}VNode;

typedef struct {

VNode adjlist[MAXV];

int n, e;

}AdjGraph;

void CreateAdj(AdjGraph*& G, int A[MAXV][MAXV], int n, int e) {

int i, j;

ArcNode* p;

G = new AdjGraph;

for (i = 0; i < n; i++)

G->adjlist[i].firstarc = NULL;

for (i = 0; i < n; i++) {

for (j = n - 1; j >= 0; j--)

if (A[i][j] != 0 && A[i][j] != INF) {

p = new ArcNode;

p->adjvex = j;

p->weight = A[i][j];

p->nextarc = G->adjlist[i].firstarc;

G->adjlist[i].firstarc = p;

}

}

G->n = n;

G->e = e;

}

void DispAdj(AdjGraph* G) {

int i;

ArcNode* p;

for (i = 0; i < G->n; i++) {

p = G->adjlist[i].firstarc;

cout << setw(3) << i;

while (p != NULL) {

cout << setw(3) << p->adjvex << "[" << p->weight << "]->";

p = p->nextarc;

}

cout << "^" << endl;

}

}

  • 栈的存储结构与基本运算算法

typedef int ElemType;

#define MaxSize 100

typedef struct {//栈的存储结构与基本运算算法

ElemType data[MaxSize];

int top;

}SqStack;

void InitStack(SqStack*& s) {

s = new SqStack;

s->top = -1;

}

void Push(SqStack*& s, ElemType e) {

if (s->top == MaxSize - 1)

return;

s->top++;

s->data[s->top] = e;

}

void Pop(SqStack*& s, ElemType& e) {

if (s->top == -1)

return;

e = s->data[s->top];

s->top--;

}

bool StackEmpty(SqStack* s) {

return s->top == -1;

}

  • 拓扑排序

拓扑排序的过程如下:               

  1. 从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。
  2. 从图中删去该顶点,并且删去从该顶点发出的全部有向边。
  3. 重复上述两步,直到剩余的图中不再存在没有前驱的顶点为止。

这样操作的结果有两种:一种是图中顶点都被输出,即该图中的所有顶点都在其拓扑序列中,这说明图中不存在回路;另一种就是图中顶点未被全部输出,这说明图中存在回路。所以可以通过对一个有向图进行拓扑排序,看是否产生全部顶点的拓扑序列来确定该图中是否存在回路。

在拓扑排序的过程中,当某个顶点的入度为0时,就将此顶点输出,同时将该顶点及其所有出边删除,实际上没有必要真正删除出边,设置一个indegree数组存放每个顶点的入度,删除一条出边是通过将出边邻接点的入度减1实现的。另外,为了避免重复检测入度为0的顶点,设置一个栈st存放人度为0的顶点,这里采用顺序栈。同时设置累加器count来判断该图是否存在回路。

void TopSort(AdjGraph* G) {//拓扑排序算法

SqStack* S;//定义栈存放顶点

InitStack(S);

int count = 0;//累加器count初始化

int indegree[MAXV];

for (int i = 0; i < G->n; i++)

indegree[i] = 0;

for (int i = 0; i < G->n; i++) {//求所有顶点的入度

ArcNode* p = G->adjlist[i].firstarc;

while (p != NULL) {

int w = p->adjvex;//找到顶点i的邻接点w

indegree[w]++;//顶点w的入度增一

p = p->nextarc;

}

}

for (int i = 0; i < G->n; i++) {//入度为0的顶点入栈

if (indegree[i] == 0)

Push(S, i);

}

int i;

while (!StackEmpty(S)) {//当栈S非空时循环

Pop(S, i);

cout << setw(3) << i;//将顶点i出栈,并访问

count++;//累加器加一

ArcNode* p = G->adjlist[i].firstarc;//寻找第一个邻接点

while (p != NULL) {//循环使顶点i的邻接点减一

int w = p->adjvex;

indegree[w]--;

if (indegree[w] == 0)//减一后将新的邻接点进栈

Push(S, w);

p = p->nextarc;

}

}

if (count < G->n)//当count<图的边数时

cout << "该图存在回路" << endl;//输出有回路信息

}

  • 主函数

    在主函数中设置了两个图,一个存在回路,一个不存在回路,可以拓扑排序。

int main() {

int n = 5, e = 7;

int A[MAXV][MAXV] = { {0,1,1,0,0},{0,0,1,0,0},{0,0,0,1,1},

{0,0,0,0,1},{1,0,0,0,0} };

int n1 = 7, e1 = 8;

int B[MAXV][MAXV] = { {0,0,1,0,0,0,0},{0,0,0,1,1,0,1},{0,0,0,1,0,0,0},

{0,0,0,0,1,1,0},{0,0,0,0,0,0,0},{0,0,0,0,0,0,0},{0,0,0,0,0,1,0} };

AdjGraph* G;

AdjGraph* G1;

CreateAdj(G, A, n, e);

cout << "图G的邻接表输出为:" << endl;

DispAdj(G);

CreateAdj(G1, B, n1, e1);

cout << "图G1的邻接表输出为:" << endl;

DispAdj(G1);

cout << "图G的拓扑排序输出为:" << endl;

TopSort(G);

cout << "图G1的拓扑排序输出为:" << endl;

TopSort(G1);

}

  • 执行结果

实 验 总 结

拓扑排序主要用来解决满足一个给定图的各个结点的顺序问题。因此如何从实际问题中抽象出图是解决问题的关键。在上述拓扑排序算法中,栈st的作用是保存当前所有入度为0的顶点,先输出其中任意哪个顶点不影响拓扑排序的正确性,所以可以用队列代替栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值