C++实现拓扑排序(AOV网络)

本文实例为大家分享了C++实现拓扑排序的具体代码,供大家参考,具体内容如下

一、思路

先扫描所有顶点,把入度为0的顶点(如C,E)进栈。然后,取栈顶元素,退栈,输出取得的栈顶元素v(即入度为0的顶点v)。接着,把顶点v的邻接顶点w的入度减1,如果w的入度变为0,则进栈。接着,取顶点w的兄弟结点(即取顶点v的邻接顶点w的下一邻接顶点),做同样的操作。重复上面步骤,直到输出n个顶点。

如上图:

(1)扫描所有顶点,把入度为0的顶点进栈:将顶点C,E进栈;

(2)取栈顶元素,退栈,输出取得的栈顶元素E。接着,把顶点E的邻接顶点A、B和F的入度减1,如果入度变为0,则进栈。因为顶点A入度变为0,所以要进栈;

(3)重复(2)步骤,直到输出n个顶点。

二、实现程序:

1.Graph.h:有向图

#ifndef Graph_h
#define Graph_h

#include <iostream>
using namespace std;

const int DefaultVertices = 30;

template <class T, class E>
struct Edge { // 边结点的定义
 int dest; // 边的另一顶点位置
 Edge<T, E> *link; // 下一条边链指针
};

template <class T, class E>
struct Vertex { // 顶点的定义
 T data; // 顶点的名字
 Edge<T, E> *adj; // 边链表的头指针
};

template <class T, class E>
class Graphlnk {
public:
 const E maxValue = 100000; // 代表无穷大的值(=∞)
 Graphlnk(int sz=DefaultVertices); // 构造函数
 ~Graphlnk(); // 析构函数
 void inputGraph(int count[]); // 建立邻接表表示的图
 void outputGraph(); // 输出图中的所有顶点和边信息
 T getValue(int i); // 取位置为i的顶点中的值
 bool insertVertex(const T& vertex); // 插入顶点
 bool insertEdge(int v1, int v2); // 插入边
 bool removeVertex(int v); // 删除顶点
 bool removeEdge(int v1, int v2); // 删除边
 int getFirstNeighbor(int v); // 取顶点v的第一个邻接顶点
 int getNextNeighbor(int v,int w); // 取顶点v的邻接顶点w的下一邻接顶点
 int getVertexPos(const T vertex); // 给出顶点vertex在图中的位置
 int numberOfVertices(); // 当前顶点数
private:
 int maxVertices; // 图中最大的顶点数
 int numEdges; // 当前边数
 int numVertices; // 当前顶点数
 Vertex<T, E> * nodeTable; // 顶点表(各边链表的头结点)
};

// 构造函数:建立一个空的邻接表
template <class T, class E>
Graphlnk<T, E>::Graphlnk(int sz) {
 maxVertices = sz;
 numVertices = 0;
 numEdges = 0;
 nodeTable = new Vertex<T, E>[maxVertices]; // 创建顶点表数组
 if(nodeTable == NULL) {
 cerr << "存储空间分配错误!" << endl;
 exit(1);
 }
 for(int i = 0; i < maxVertices; i++)
 nodeTable[i].adj = NULL;
}

// 析构函数
template <class T, class E>
Graphlnk<T, E>::~Graphlnk() {
 // 删除各边链表中的结点
 for(int i = 0; i < numVertices; i++) {
 Edge<T, E> *p = nodeTable[i].adj; // 找到其对应链表的首结点
 while(p != NULL) { // 不断地删除第一个结点
  nodeTable[i].adj = p->link;
  delete p;
  p = nodeTable[i].adj;
 }
 }
 delete []nodeTable; // 删除顶点表数组
}

// 建立邻接表表示的图
template <class T, class E>
void Graphlnk<T, E>::inputGraph(int count[]) {
 int n, m; // 存储顶点树和边数
 int i, j, k;
 T e1, e2; // 顶点

 cout << "请输入顶点数和边数:" << endl;
 cin >> n >> m;
 cout << "请输入各顶点:" << endl;
 for(i = 0; i < n; i++) {
 cin >> e1;
 insertVertex(e1); // 插入顶点
 }

 cout << "请输入图的各边的信息:" << endl;
 i = 0;
 while(i < m) {
 cin >> e1 >> e2;
 j = getVertexPos(e1);
 k = getVertexPos(e2);
 if(j == -1 || k == -1)
  cout << "边两端点信息有误,请重新输入!" << endl;
 else {
  insertEdge(j, k); // 插入边
  count[k]++; // 记录入度
  i++;
 }
 } // while
}

// 输出有向图中的所有顶点和边信息
template <class T, class E>
void Graphlnk<T, E>::outputGraph() {
 int n, m, i;
 T e1, e2; // 顶点
 Edge<T, E> *p;

 n = numVertices;
 m = numEdges;
 cout << "图中的顶点数为" << n << ",边数为" << m << endl;
 for(i = 0; i < n; i++) {
 p = nodeTable[i].adj;
 while(p != NULL) {
  e1 = getValue(i); // 有向边<i, p->dest>
  e2 = getValue(p->dest);
  cout << "<" << e1 << ", " << e2 << ">" << endl;
  p = p->link; // 指向下一个邻接顶点
 }
 }
}

// 取位置为i的顶点中的值
template <class T, class E>
T Graphlnk<T, E>::getValue(int i) {
 if(i >= 0 && i < numVertices)
 return nodeTable[i].data;
 return NULL;
}

// 插入顶点
template <class T, class E>
bool Graphlnk<T, E>::insertVertex(const T& vertex) {
 if(numVertices == maxVertices) // 顶点表满,不能插入
 return false;
 nodeTable[numVertices].data = vertex; // 插入在表的最后
 numVertices++;
 return true;
}

// 插入边
template <class T, class E>
bool Graphlnk<T, E>::insertEdge(int v1, int v2) {
 if(v1 == v2) // 同一顶点不插入
 return false;
 if(v1 >= 0 && v1 < numVertices && v2 >= 0 && v2 < numVertices) {
 Edge<T, E> *p = nodeTable[v1].adj; // v1对应的边链表头指针
 while(p != NULL && p->dest != v2) // 寻找邻接顶点v2
  p = p->link;
 if(p != NULL) // 已存在该边,不插入
  return false;
 p = new Edge<T, E>; // 创建新结点
 p->dest = v2;
 p->link = nodeTable[v1].adj; // 链入v1边链表
 nodeTable[v1].adj = p;
 numEdges++;
 return true;
 }
 return false;
}

// 有向图删除顶点较麻烦
template <class T, class E>
bool Graphlnk<T, E>::removeVertex(int v) {
 if(numVertices == 1 || v < 0 || v > numVertices)
 return false; // 表空或顶点号超出范围

 Edge<T, E> *p, *s;
 // 1.清除顶点v的边链表结点w 边<v,w>
 while(nodeTable[v].adj != NULL) {
 p = nodeTable[v].adj;
 nodeTable[v].adj = p->link;
 delete p;
 numEdges--; // 与顶点v相关联的边数减1
 } // while结束
 // 2.清除<w, v>,与v有关的边
 for(int i = 0; i < numVertices; i++) {
 if(i != v) { // 不是当前顶点v
  s = NULL;
  p = nodeTable[i].adj;
  while(p != NULL && p->dest != v) {// 在顶点i的链表中找v的顶点
  s = p;
  p = p->link; // 往后找
  }
  if(p != NULL) { // 找到了v的结点
  if(s == NULL) { // 说明p是nodeTable[i].adj
   nodeTable[i].adj = p->link;
  } else {
   s->link = p->link; // 保存p的下一个顶点信息
  }
  delete p; // 删除结点p
  numEdges--; // 与顶点v相关联的边数减1
  }
 }
 }
 numVertices--; // 图的顶点个数减1
 nodeTable[v].data = nodeTable[numVertices].data; // 填补,此时numVertices,比原来numVertices小1,所以,这里不需要numVertices-1
 nodeTable[v].adj = nodeTable[numVertices].adj;
 // 3.要将填补的顶点对应的位置改写
 for(int i = 0; i < numVertices; i++) {
 p = nodeTable[i].adj;
 while(p != NULL && p->dest != numVertices) // 在顶点i的链表中找numVertices的顶点
  p = p->link; // 往后找
 if(p != NULL) // 找到了numVertices的结点
  p->dest = v; // 将邻接顶点numVertices改成v
 }
 return true;
}

// 删除边
template <class T, class E>
bool Graphlnk<T, E>::removeEdge(int v1, int v2) {
 if(v1 != -1 && v2 != -1) {
 Edge<T, E> * p = nodeTable[v1].adj, *q = NULL;
 while(p != NULL && p->dest != v2) { // v1对应边链表中找被删除边
  q = p;
  p = p->link;
 }
 if(p != NULL) { // 找到被删除边结点
  if(q == NULL) // 删除的结点是边链表的首结点
  nodeTable[v1].adj = p->link;
  else
  q->link = p->link; // 不是,重新链接
  delete p;
  return true;
 }
 }
 return false; // 没有找到结点
}

// 取顶点v的第一个邻接顶点
template <class T, class E>
int Graphlnk<T, E>::getFirstNeighbor(int v) {
 if(v != -1) {
 Edge<T, E> *p = nodeTable[v].adj; // 对应链表第一个边结点
 if(p != NULL) // 存在,返回第一个邻接顶点
  return p->dest;
 }
 return -1; // 第一个邻接顶点不存在
}

// 取顶点v的邻接顶点w的下一邻接顶点
template <class T, class E>
int Graphlnk<T, E>::getNextNeighbor(int v,int w) {
 if(v != -1) {
 Edge<T, E> *p = nodeTable[v].adj; // 对应链表第一个边结点
 while(p != NULL && p->dest != w) // 寻找邻接顶点w
  p = p->link;
 if(p != NULL && p->link != NULL)
  return p->link->dest; // 返回下一个邻接顶点
 }
 return -1; // 下一个邻接顶点不存在
}

// 给出顶点vertex在图中的位置
template <class T, class E>
int Graphlnk<T, E>::getVertexPos(const T vertex) {
 for(int i = 0; i < numVertices; i++)
 if(nodeTable[i].data == vertex)
  return i;
 return -1;
}

// 当前顶点数
template <class T, class E>
int Graphlnk<T, E>::numberOfVertices() {
 return numVertices;
}

#endif /* Graph_h */

2.TopLogicalSort.h

#ifndef TopLogicalSort_h
#define TopLogicalSort_h
#include "Graph.h"

template <class T, class E>
void TopLogicalSort(Graphlnk<T, E> &G) {
 int i, w, v;
 int n; // 顶点数
 int *count = new int[DefaultVertices]; // 入度数组
 int top = -1;

 // 清零
 for(i = 0; i< DefaultVertices; i++)
 count[i] = 0;
 // 输入顶点和边
 G.inputGraph(count);
 n = G.numberOfVertices(); // 获取图的顶点数
 for(i = 0; i < n; i++) { // 检查网络所有顶点
 if(count[i] == 0) { // 入度为0的顶点进栈
  count[i] = top;
  top = i;
 }
 }
 // 进行拓扑排序,输出n个顶点
 for(i = 0; i < n; i++) {
 if(top == -1) { // 空栈
  cout << "网络中有回路!" << endl;
  return;
 } else {
  v = top;
  top = count[top];
  cout << G.getValue(v) << " "; // 输出入度为0的顶点
  w = G.getFirstNeighbor(v); // 邻接顶点
  while(w != -1) { // 扫描出边表
  if(--count[w] == 0) { // 邻接顶点入度减1,如果入度为0则进栈
   count[w] = top;
   top = w;
  }
  w = G.getNextNeighbor(v, w); // 兄弟结点(取顶点v的邻接顶点w的下一邻接顶点)
  }
 }
 }
 cout << endl;
}

#endif /* TopLogicalSort_h */
3.main.cpp

#include "TopLogicalSort.h"

int main(int argc, const char * argv[]) {
 Graphlnk<char, int> G; // 声明图对象

 TopLogicalSort(G); // AOV网络的拓扑排序
 return 0;
}

测试数据:

6 8
A B C D E F
A B
A D
B F
C B
C F
E A
E F
E B

测试结果:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C++实现希尔排序(ShellSort)

    本文实例为大家分享了C++实现希尔排序的具体代码,供大家参考,具体内容如下 一.思路: 希尔排序:又称缩小增量排序,是一种改进的插入排序算法,是不稳定的. 设排序元素序列有n个元素,首先取一个整数gap<n作为间隔,将全部元素分为gap个子序列,所有距离为gap的元素放在同一个子序列中,在每一个子序列中分别施行直接插入排序.然后缩小间隔gap,重复上述的子序列和排序工作. 二.实现程序: #include <iostream> using namespace std; const int

  • C++实现归并排序(MergeSort)

    本文实例为大家分享了C++实现归并排序的具体代码,供大家参考,具体内容如下 一.思路:稳定排序 (1)划分:一直调用划分过程,直到子序列为空或只有一个元素为止,共需log2(n): (2)归并:将两个子序列从小到大合并为一个序列 二.实现程序: // 归并排序:(二路归并) // (1)递归分解数组: // (2)合并有序的序列 #include <iostream> using namespace std; // 合并两个有序的序列 template <typename T> v

  • C++实现冒泡排序(BubbleSort)

    本文实例为大家分享了C++实现冒泡排序的具体代码,供大家参考,具体内容如下 一.思路: 冒泡排序算法原理: 1.比较相邻的元素.如果第一个数比第二个数大,就交换他们两个. 2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 3.针对所有的元素重复以上的步骤,除了最后一个.(因为最后一个已经排好,是最大的数) 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较.(接着排第二大的数,一直下去) 如:使用冒泡排序:25 16 9

  • C++实现选择排序(selectionSort)

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 一.思路 每次取剩下没排序的数中的最小数,然后,填到对应位置.(可以使用a[0]位置作为暂存单元) 如下: 二.实现程序 #include <iostream> using namespace std; const int maxSize = 100; template<class T> void SelectSort(T arr[], int n); // 选择排序 int main(int a

  • C++实现折半插入排序(BinaryInsertSort)

    本文实例为大家分享了C++实现折半插入排序的具体代码,供大家参考,具体内容如下 一.思路: 较插入排序,减少了比较的次数,但是插入时间还是一样. (1)按二分查找的方法,查找V[i]在V[0],V[1]-V[i-1]中插入的位置: (2)将插入位置的元素向后顺移. 二.实现程序: // 二分插入:较插入排序,减少了比较的次数,但是插入时间还是一样 // 时间复杂度还是:O(n*n) #include <iostream> using namespace std; const int maxSi

  • C++实现选择性排序(SelectionSort)

    "选择性排序"是数列排序的算法之一. 其思路引点来源于经典的"可乐雪碧问题" "现有两杯饮料,一杯是雪碧,一杯是可乐,试问如何可以将两杯饮料交换?" "答:最简单的解决方案就是利用一个空杯,创造一个缓存区." 选择性排序就是利用线性搜索数列并找到当前最小值,通过不断的将当前最小值放置当前位置索引的算法. 1.算法思路 这是一个未排序的数列. 首先,线性搜索数列,找到最小值. 将最小值替换为列中左端的数字并进行排序,如果最小值已

  • C++实现拓扑排序(AOV网络)

    本文实例为大家分享了C++实现拓扑排序的具体代码,供大家参考,具体内容如下 一.思路 先扫描所有顶点,把入度为0的顶点(如C,E)进栈.然后,取栈顶元素,退栈,输出取得的栈顶元素v(即入度为0的顶点v).接着,把顶点v的邻接顶点w的入度减1,如果w的入度变为0,则进栈.接着,取顶点w的兄弟结点(即取顶点v的邻接顶点w的下一邻接顶点),做同样的操作.重复上面步骤,直到输出n个顶点. 如上图: (1)扫描所有顶点,把入度为0的顶点进栈:将顶点C,E进栈: (2)取栈顶元素,退栈,输出取得的栈顶元素E

  • 详解图的应用(最小生成树、拓扑排序、关键路径、最短路径)

    1.最小生成树:无向连通图的所有生成树中有一棵边的权值总和最小的生成树 1.1 问题背景: 假设要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路.这时,自然会考虑这样一个问题,如何在最节省经费的前提下建立这个通信网.在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价.n个城市之间,最多可能设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少呢? 1.2 分析问题(建立模型): 可以用连通网来表示n个城市以及n个城市间可能设置的通信线

  • python实现拓扑排序的基本教程

    拓扑排序 几乎在所有的项目,甚至日常生活,待完成的不同任务之间通常都会存在着某些依赖关系,这些依赖关系会为它们的执行顺序行程表部分约束.对于这种依赖关系,很容易将其表示成一个有向无环图(Directed Acyclic Graph,DAG,无环是一个重要条件),并将寻找其中依赖顺序的过程称为拓扑排序(topological sorting). 拓扑排序要满足如下两个条件 每个顶点出现且只出现一次. 若A在序列中排在B的前面,则在图中不存在从B到A的路径. 拓扑排序算法 任何无回路的顶点活动网(A

  • 详解C++实现拓扑排序算法

    一.拓扑排序的介绍 拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行.为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行.通常,我们把这种顶点表示活动.边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简

  • 详解Java实现拓扑排序算法

    目录 一.介绍 二.拓扑排序算法分析 三.拓扑排序代码实现 一.介绍 百科上这么定义的: 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前.通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列.简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序. 为什么会有拓扑排序?拓

  • C++详细讲解图的拓扑排序

    目录 一.前言 二.算法流程 三.有向图的拓扑排序 一.前言 且该序列必须满足下面两个条件: 每个顶点出现且只出现一次. 若存在一条从顶点 x到顶点 y的路径,那么在序列中顶点 x 出现在顶点 y的前面. 拓扑排序只适用于 AOV网 (有向无环图) 若图中有环,则一定不存在拓扑序. 可以证明,一个有向无环图,一定存在一个拓扑序列.有向无环图,又被称为拓扑图. 入度: 即有多少条边指向自己这个节点. 出度: 即有多少条边从自己这个节点指出去. 二.算法流程 算法流程: 用队列来执行 ,初始化所有入

  • C/C++浅析邻接表拓扑排序算法的实现

    目录 前言 一.拓扑排序算法的思路 二.实现步骤 1.求个顶点的入度 2.拓扑排序的实现 三.测试结果 总结 前言 在软件开发.施工过程.教学安排等等的一系列活动中,往往需要一个有向无环图来表示其是否成成功进行下去. 在一个有向图为顶点表示活动的网中,我们称为AOV网(Activity On Vertex Network).设G={V,E}是一个具有n个顶点的有向图,V中的顶点序列v1,v2,…,vn,满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在vj之前.则我们称这样的顶点为一个

  • Java实现拓扑排序算法的示例代码

    目录 拓扑排序原理 1.点睛 2.拓扑排序 3.算法步骤 4.图解 拓扑排序算法实现 1.拓扑图 2.实现代码 3.测试 拓扑排序原理 1.点睛 一个无环的有向图被称为有向无环图.有向无环图是描述一个工程.计划.生产.系统等流程的有效工具.一个大工程可分为若干子工程(活动),活动之间通常有一定的约束,例如先做什么活动,有什么活动完成后才可以开始下一个活动. 用节点表示活动,用弧表示活动之间的优先关系的有向图,被称为 AOV 网. 在 AOV 网中,若从节点 i 到节点 j 存在一条有向路径,则称

  • Golang实现拓扑排序(DFS算法版)

    问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来.要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现. 该问题是一个非常典型的拓扑排序的问题,一般解决拓扑排序的方案是采用DFS-深度优先算法,对于DFS算法我的浅薄理解就是递归,因拓扑排序问题本身会有一些前置条件(本文不过多介绍拓扑算法的定义),所以解决该问题就有了以下思路. 先将排序要求声明成map(把map的key,value看作对顺序的要求,key应在value前出现),然后遍历1-5这几个数,将每次遍

  • Python关于拓扑排序知识点讲解

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前. 通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列.简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序. 在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topolog

随机推荐