本章是通过C++实现拓扑排序。

目录
1. 拓扑排序介绍
2. 拓扑排序的算法图解
3. 拓扑排序的代码说明
4. 拓扑排序的完整源码和测试程序

转载请注明出处:http://www.cnblogs.com/skywang12345/

更多内容:数据结构与算法系列 目录

拓扑排序介绍

拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。

这样说,可能理解起来比较抽象。下面通过简单的例子进行说明!
例如,一个项目包括A、B、C、D四个子部分来完成,并且A依赖于B和D,C依赖于D。现在要制定一个计划,写出A、B、C、D的执行顺序。这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的。

在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面。

拓扑排序的算法图解

拓扑排序算法的基本步骤:

1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);
2. 把所有没有依赖顶点的节点放入Q;
3. 当Q还有顶点的时候,执行下面步骤:
3.1 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);
3.2 对n每一个邻接点m(n是起点,m是终点);
3.2.1 去掉边<n,m>;
3.2.2 如果m没有依赖顶点,则把m放入Q;
注:顶点A没有依赖顶点,是指不存在以A为终点的边。

以上图为例,来对拓扑排序进行演示。

第1步:将B和C加入到排序结果中。
    顶点B和顶点C都是没有依赖顶点,因此将C和C加入到结果集T中。假设ABCDEFG按顺序存储,因此先访问B,再访问C。访问B之后,去掉边<B,A>和<B,D>,并将A和D加入到队列Q中。同样的,去掉边<C,F>和<C,G>,并将F和G加入到Q中。
    (01) 将B加入到排序结果中,然后去掉边<B,A>和<B,D>;此时,由于A和D没有依赖顶点,因此并将A和D加入到队列Q中。
    (02) 将C加入到排序结果中,然后去掉边<C,F>和<C,G>;此时,由于F有依赖顶点D,G有依赖顶点A,因此不对F和G进行处理。
第2步:将A,D依次加入到排序结果中。
    第1步访问之后,A,D都是没有依赖顶点的,根据存储顺序,先访问A,然后访问D。访问之后,删除顶点A和顶点D的出边。
第3步:将E,F,G依次加入到排序结果中。

因此访问顺序是:B -> C -> A -> D -> E -> F -> G

拓扑排序的代码说明

拓扑排序是对有向无向图的排序。下面以邻接表实现的有向图来对拓扑排序进行说明。

1. 基本定义

#define MAX 100
// 邻接表
class ListDG
{
private: // 内部类
// 邻接表中表对应的链表的顶点
class ENode
{
int ivex; // 该边所指向的顶点的位置
ENode *nextEdge; // 指向下一条弧的指针
friend class ListDG;
}; // 邻接表中表的顶点
class VNode
{
char data; // 顶点信息
ENode *firstEdge; // 指向第一条依附该顶点的弧
friend class ListDG;
}; private: // 私有成员
int mVexNum; // 图的顶点的数目
int mEdgNum; // 图的边的数目
VNode *mVexs; // 图的顶点数组 public:
// 创建邻接表对应的图(自己输入)
ListDG();
// 创建邻接表对应的图(用已提供的数据)
ListDG(char vexs[], int vlen, char edges[][2], int elen);
~ListDG(); // 深度优先搜索遍历图
void DFS();
// 广度优先搜索(类似于树的层次遍历)
void BFS();
// 打印邻接表图
void print();
// 拓扑排序
int topologicalSort(); private:
// 读取一个输入字符
char readChar();
// 返回ch的位置
int getPosition(char ch);
// 深度优先搜索遍历图的递归实现
void DFS(int i, int *visited);
// 将node节点链接到list的最后
void linkLast(ENode *list, ENode *node);
};

(01) ListDG是邻接表对应的结构体。 mVexNum是顶点数,mEdgNum是边数;mVexs则是保存顶点信息的一维数组。
(02) VNode是邻接表顶点对应的结构体。 data是顶点所包含的数据,而firstEdge是该顶点所包含链表的表头指针。
(03) ENode是邻接表顶点所包含的链表的节点对应的结构体。 ivex是该节点所对应的顶点在vexs中的索引,而nextEdge是指向下一个节点的。

2. 拓扑排序

/*
* 拓扑排序
*
* 返回值:
* -1 -- 失败(由于内存不足等原因导致)
* 0 -- 成功排序,并输入结果
* 1 -- 失败(该有向图是有环的)
*/
int ListDG::topologicalSort()
{
int i,j;
int index = 0;
int head = 0; // 辅助队列的头
int rear = 0; // 辅助队列的尾
int *queue; // 辅组队列
int *ins; // 入度数组
char *tops; // 拓扑排序结果数组,记录每个节点的排序后的序号。
ENode *node; ins = new int[mVexNum];
queue = new int[mVexNum];
tops = new char[mVexNum];
memset(ins, 0, mVexNum*sizeof(int));
memset(queue, 0, mVexNum*sizeof(int));
memset(tops, 0, mVexNum*sizeof(char)); // 统计每个顶点的入度数
for(i = 0; i < mVexNum; i++)
{
node = mVexs[i].firstEdge;
while (node != NULL)
{
ins[node->ivex]++;
node = node->nextEdge;
}
} // 将所有入度为0的顶点入队列
for(i = 0; i < mVexNum; i ++)
if(ins[i] == 0)
queue[rear++] = i; // 入队列 while (head != rear) // 队列非空
{
j = queue[head++]; // 出队列。j是顶点的序号
tops[index++] = mVexs[j].data; // 将该顶点添加到tops中,tops是排序结果
node = mVexs[j].firstEdge; // 获取以该顶点为起点的出边队列 // 将与"node"关联的节点的入度减1;
// 若减1之后,该节点的入度为0;则将该节点添加到队列中。
while(node != NULL)
{
// 将节点(序号为node->ivex)的入度减1。
ins[node->ivex]--;
// 若节点的入度为0,则将其"入队列"
if( ins[node->ivex] == 0)
queue[rear++] = node->ivex; // 入队列 node = node->nextEdge;
}
} if(index != mVexNum)
{
cout << "Graph has a cycle" << endl;
delete queue;
delete ins;
delete tops;
return 1;
} // 打印拓扑排序结果
cout << "== TopSort: ";
for(i = 0; i < mVexNum; i ++)
cout << tops[i] << " ";
cout << endl; delete queue;
delete ins;
delete tops; return 0;
}

说明:
(01) queue的作用就是用来存储没有依赖顶点的顶点。它与前面所说的Q相对应。
(02) tops的作用就是用来存储排序结果。它与前面所说的T相对应。

拓扑排序的完整源码和测试程序

拓扑排序源码(ListDG.cpp)

拓扑排序(二)之 C++详解的更多相关文章

  1. 拓扑排序(三)之 Java详解

    前面分别介绍了拓扑排序的C和C++实现,本文通过Java实现拓扑排序. 目录 1. 拓扑排序介绍 2. 拓扑排序的算法图解 3. 拓扑排序的代码说明 4. 拓扑排序的完整源码和测试程序 转载请注明出处 ...

  2. MySQL慢查询(二) - pt-query-digest详解慢查询日志 pt-query-digest 慢日志分析

    随笔 - 66 文章 - 0 评论 - 19 MySQL慢查询(二) - pt-query-digest详解慢查询日志 一.简介 pt-query-digest是用于分析mysql慢查询的一个工具,它 ...

  3. 数据结构图文解析之:二叉堆详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  4. Java进阶(三十二) HttpClient使用详解

    Java进阶(三十二) HttpClient使用详解 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们 ...

  5. Spring Boot 启动(二) 配置详解

    Spring Boot 启动(二) 配置详解 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Boot 配置 ...

  6. Android ADB命令教程二——ADB命令详解

    Android ADB命令教程二——ADB命令详解 转载▼ 原文链接:http://www.tbk.ren/article/249.html       我们使用 adb -h 来看看,adb命令里面 ...

  7. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  8. [hihoCoder] 第四十八周: 拓扑排序·二

    题目1 : 拓扑排序·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho所在学校的校园网被黑客入侵并投放了病毒.这事在校内BBS上立刻引起了大家的讨论,当 ...

  9. hiho一下 第四十八周 拓扑排序·二【拓扑排序的应用 + 静态数组 + 拓扑排序算法的时间优化】

    题目1 : 拓扑排序·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho所在学校的校园网被黑客入侵并投放了病毒.这事在校内BBS上立刻引起了大家的讨论,当 ...

随机推荐

  1. 测试架构图 High Level 产品技术(无事来更新,证明这个博客还是Live的)

    一个完整的产品测试所需要掌握的产品技术架构. 1.最底层硬件平台(服务器与存储) 对于一个大型商业解决方案来说,性能与可靠性是非常重要的要求,那么服务器与存储就是专门来满足需求的. 服务器: 服务器端 ...

  2. Android中的PopupWindow

    1.功能 PopupWindow这个类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的,可以设置显示位置. 2.需求 弹出软键盘,实现键盘功能从而 ...

  3. IOS 开发下拉刷新和上拉加载更多

    IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh ( ...

  4. 用Win7自带的磁盘管理工具给硬盘分区

    最近新买了一台笔记本,要给硬盘分几个区,心想还是用个工具方便点,于是就上网准备下个“硬盘分区魔术师”,但是看到有一篇文章介绍Win7系统也自带了硬盘分区工具,这我以前倒没听说过,试了一下,还挺方便好用 ...

  5. Linux环境下Oracle数据库启动停止命令

    切换root至oracle数据库账号 su – oracle 查看数据库服务状态: ps -ef |grep oracle netstat –an|grep 1521 查看数据库监听状态: [orac ...

  6. 开源库Magicodes.ECharts使用教程

    目录 1    概要    2 2    Magicodes.ECharts工作原理    3 2.1    架构说明    3 2.1.1    Axis    4 2.1.2    CommonD ...

  7. StrongSwan 5.1.1 发布,Linux 的 IPsec 项目

    StrongSwan是一个完整的2.4和2.6的Linux内核下的IPsec和IKEv1 的实现.它也完全支持新的IKEv2协议的Linux 2.6内核.结合IKEv1和IKEv2模式与大多数其他基于 ...

  8. Vs2013 头文件注释

    在vs2013的默认安装目录 1.CS类修改方式 在C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ItemTempla ...

  9. 设计模式之美:Manager(管理器)

    索引 意图 结构 参与者 适用性 效果 实现 实现方式(一):Manager 模式的示例实现. 意图 将对一个类的所有对象的管理封装到一个单独的管理器类中. 这使得管理职责的变化独立于类本身,并且管理 ...

  10. angularjs移除不必要的$watch

    在我们的web page,特别是移动设备上,太多的angular $watch将可能导致性能问题.这篇文章将解释如何去移除额外的$watch提高你的应用程序性能. $watch如果不再使用,我们最好将 ...