1.拓补排序

1.有向无环图
如果一个有向图的任意顶点都无法通过一些有向边回到自身,那么称这个有向图为有向无环图(Airected Acyclic Graph,ADG)。

AOV网(Activity On Vertex,AOV):指用顶点表示活动,而用边集表示活动间优先关系的有向图,例如下图中数学先导课程示意图就是AOV网,其中图的顶点表示各项课程,也就是“活动”;有向边表示课程的先导关系,也就是“活动间的优先关系”。显然,图中不应该存在有向环,否则会让优先关系出现逻辑错误,任何活动不能以它自己作为自己的前驱和后继。

2.拓补排序

拓补排序是将有向无环图G的所有顶点排成一个线性序列,使得对图G中的任意两个顶点u、v,如果存在边u->v,那么在序列中u一定在v前面。这个序列称为拓补排序。

以下图数学专业的某几门课程学习先后顺序为例,可以获知, “数学分析”是“复变函数”、“常微分方程”、“计算方法”的先导课程,“复变函数”是“实变函数”和“泛函函数”的先导课程,“实变函数”又是“泛函分析”的先导课程。显然,对一门课程来说,必须要先学习它的先导课程才能很好地学习这门课程,而且课程之间不能够形成环(如果“泛函分析”同时又是“空间解析几何”的先导课程,就乱套了)。

同时发现,如果课程之间没有直接或间接的先导关系,那么这两门学习的先后顺序是任意的,(例如“复变函数”与“计算方法”的学习顺序就是任意的)。于是可以把上面的课程排成一个学习的先后序列,使得这个序列中的课程顺序满足下图中的课程顺序。

拓补排序步骤如下:
(1)定义一个队列Q,并把所有入度为0的结点加入队列。
(2)取队首结点,输出,然后删去所有从它出发的边,并令这些边到达的顶点的入度减1,如果某个顶点的入度减为 0,则将其加入队列。
(3)反复进行(2)操作,直到队列为空,如果队列为空时入过队列的结点数目恰好为N,说明拓补排序成功,图G为有向无环图;否则,拓补排序失败,图G中有环。

由于需要记录结点的入度,因此需要额外建立一个数组inDegree[MAXV],并在程序一开始读入图时就记录好每个结点的入度。

拓补排序的代码如下:

vector<int> G[MAXV];   //邻接表
int n, m, inDgree[MAXV];   // 顶点数、入度//拓补排序
bool topologicalSort(){int num = 0;  //记录加入拓补排序的顶点数queue<int> q;for(int i=0; i<n; i++){if(inDegree[i] == 0){q.push(i);     //将所有入度为0的顶点入队 } }  while(!q.empty()){int u = q.front();  //取队首顶点q.pop();for(int i=0; i<n; i++){int v = G[u][i];   //u的后继结点vinDegree[v]--;     //顶点v的入度减1if(inDegree[v] == 0){q.push(v);} } G[u].clear();   //清空顶点u的所有出边num++;    //加入拓补排序的顶点数加1 } if(num == n) return true;  //加入拓补排序的顶点数为n,说明拓补排序成功else return false;   //加入拓补排序的顶点数小于n,说明拓补排序失败
}

拓补排序很重要的应用就是判断一个给定的图是否是有向无环图,正如上面的代码,如果topologicalSort()函数返回true,则说明拓补排序成功,给定的图是有向无环图;否则,说明拓补排序失败,给定的图中有环。如果要求有多个入度为0的顶点,选择编号最小的顶点,那么把queue改成priority_queue,并保持队首元素(堆顶元素)是优先队列中最小的元素。

2.并查集

1.定义

并查集是一种维护集合的数据结构,它的名字中“并”、“查”、“集”分别取自Union(合并)、Find(查找)、Set(集合)这3个单词。并查集对于kuskal算法中如何判断测试边的两个端点是否在不同连通块中和如何将测试边加入最小生成树中有非常巧妙的运用。

并查集是用数组实现的,int father[N],其中father[i]表示元素i的父亲结点,而父亲结点本身也是这个集合内的元素(1 <= i <=N)。例如father[1] = 2就表示元素1的父亲结点是元素2,以这种父系关系来表示元素所属的集合。如果father[i] == i,说明元素i是该集合的根节点,但对同一个集合来说只存在一个根节点,且将作为所属集合的标识。

2.并查集的基本操作

并查集的使用需要先初始化father[]数组,然后再根据需要进行查找操作。

(1)初始化
一开始,每个元素都是独立的一个集合,因此需要令所有father[i]等于i:

for(int i=1; i<=n; i++){father[i] = i;
}

(2)查找
由于规定同一个集合中只存在一个根节点,因此查找操作就是对给定的结点寻找其根节点的过程。实现的方式可以是递归或是递推,思路都是一样的,反复寻找父亲结点,直到找到根节点(即father[i] == i的结点)。
递推代码如下:

//findFather函数返回元素x所在集合的根结点
int findFather(int x){while(x != father[x]){  //如果不是根结点,继续循环 x = father[x];//获得自己的父亲结点 }return x;
}

递归代码如下:

inf findFather(int x){if(x == father[x])  return x;  //找到根节点,返回根节点编号else  return findFather(father[x]);  //否则,递归判断x的父亲结点是否是根节点
}

(3)合并

合并是指把两个集合合并成一个集合,题目中一般给出两个元素,要求把这两个元素所在集合合并。具体实现上一般是先判断两个元素是否属于同一个集合,只有当两个元素属于不同集合时才合并,而合并的过程一般是把其中一个集合的根节点的父亲结点指向另一个集合的根节点。

思路如下:

  1. 对于给定的两个元素a、b,判断它们是否属于同一集合。可以调用上面的查找函数,对这两个元素a、b分别查找根节点,然后再判断其根节点是否相同。
  2. 合并两个集合:在第一步中已经获得两个元素的根节点faA和faB,因此只需要把其中一个父亲节点指向另一个结点,例如可以令father[faA] = faB。

合并代码如下:

void Union(int a, int b){int faA = findFather(a);   // 查找a的根结点,记为faAint faB = findFather(b);   // 查找b的根结点,记为faBif(faA != faB){father[faA] = faB;  //合并它们 }
}

3.路径压缩

上面的并查集查找函数没有优化,如果题目给出的元素数量很多且形成一条链,那么这个查找函数的效率就会非常低。如下图所示,总共有105个元素形成一条链,那么假设要进行105查询,且每次查询都查询最后面的结点的根节点,那么每次都要花费105的计算量查找,这显然无法承受。

由于findFather函数的目的就是查找根节点,例如下面这个例子:

father[1] = 1;
father[2] = 1;
father[3] = 2;
father[4] = 3;

因此,如果只是为了查找根结点,那么完全可以想办法把操作等价地变成:

father[1] = 1;
father[2] = 1;
father[3] = 1;
father[4] = 1;

对应的图形变化如下:

这样相当于把当前查询结点的路径上的所有结点的父亲都指向根节点,查找的时候就不需要一直回溯去找父亲结点了 ,查询的复杂度为O(1)。

转换过程步骤如下:
(1)按原先的写法获得x的根节点r。
(2)重新从x开始走一遍寻找根节点的过程,把路径上经过的所有结点的父亲结点全部改成根节点r。
代码如下:

int findFather(int x){//由于x在下面的while中会变成根节点,因此先把原先的x保存一下int a = x;while(x != father[x]){// 寻找根节点x = father[x]; } //到这里,x存放的是根节点。下面把路径上的所有结点的father都改成根节点while(a != father[a]) {int z = a;  //因为a要被father[a]覆盖,所以先保存a的值,已修改father[a] a = father[a];  //a回溯父亲结点 father[z] = x;  //将原先的结点a的父亲改成根节点 }return x; //返回根节点
}

递归代码如下:

int findFathe(int v){if(v == father[v])  return v;//找到根节点else{int F = findFather(father[v]);  //递归寻找father[v]的根节点Fint father[v] = F;  //将根节点F赋给father[v]return F;   //返回根结点F }
}

数据结构之——拓补排序和并查集相关推荐

  1. 【BZOJ3036】绿豆蛙的归宿 拓补排序+概率

    [BZOJ3036]绿豆蛙的归宿 Description 随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿. 给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度. ...

  2. 奖金(拓补排序的应用)

    谁应该高谁的入度++,并记录下谁比低的高(低的得出度),所以入度为零的就是最低的(好不公平),找出所有最低的,将他们算作一层,奖金++(算是拓补排序吧) #include<cstdio> ...

  3. HDU4324 - Triangle LOVE 拓补排序

    HDU4324 - Triangle LOVE : http://acm.showproblemhdu.edu.cn/.php?pid=4324 标准的拓补排序,上代码 : #include < ...

  4. leetcode *210. 课程表 II(拓补排序)(2020.5.17)

    [题目]*210. 课程表 II 现在你总共有 n 门课需要选,记为 0 到 n-1. 在选修某些课程之前需要一些先修课程. 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们 ...

  5. 51nod-生产口罩(拓补排序+DP)by zyz

    题目:生产口罩 链接:http://class.51nod.com/Classes/Problem.html#courseProblemId=1718&classId=129 //注:题目来自 ...

  6. Aov网络与拓补排序的实现

    测试的节点分布如下: 测试代码如下: /** 拓补排序的实现,使用邻接链表存储有向图 */ #include <iostream> #include <cstdio> #inc ...

  7. 每日一题30:拓补排序

    问题描述 所谓拓补排序就是确定图中节点的一种顺序,使得某些在别的节点访问之前不能访问到的节点排在后面.所以该算法的核心是每一步选择一个没有入度的节点,因为没有入度意味着该节点没有前驱,得到一个节点后, ...

  8. 士兵排队问题(拓补排序)(附简要拓补排序思想及算法)

    题目描述 有N个士兵(1<=N<=100),编号依次为1,2,...,N.队列训练时,指挥官要把士兵从高到矮排成一行,但指挥官只知道"1 比2 高,7 比 5高"这样的 ...

  9. 后缀自动机求多个串的最长公共子串+拓补排序讲解+LCS2 - Longest Common Substring II

    网上所有关于后缀自动机拓补排序的文章,都默认读者会拓补排序,简直了. 后缀自动机的拓补排序,就是按照长度进行排序,在进行特定操作的时候,通过较长的后缀来更新较短的后缀.那么也就是通过拓补排序中排名靠后 ...

  10. POJ 1094拓补排序

    POJ 1094拓补排序问题,需要加一些判断 #include <cstdio> #include <queue> #include <vector> #inclu ...

最新文章

  1. Python 在腾讯研发排第 5,鹅厂 2019 年新增 12.9 亿行代码
  2. Compute节点无法启动nova组件,错误信息:AMQP server on 127.0.0.1:5672 is unreachable
  3. 【Unity】Protobuf的使用与常见问题
  4. drupal安装教程 linux,如何在Fedora Linux上安装Drupal 7
  5. 事务、事件(文件、时间、调度和执行)、复制、分片(范围、哈希)、简单的论坛系统分析
  6. 8266串口调试助手_开源软件分享-基于WPF的串口调试工具
  7. [转]C++中的static关键字的总结
  8. 【C/C 】浅谈C/C 中函数指针与回调函数
  9. 还要什么ETL?它是搭建数据仓库的必备,许多人都不知道!
  10. C#LeetCode刷题之#541-反转字符串 II(Reverse String II)
  11. 智能标注、电力和地下管网巡检,CV算法落地方案
  12. __RESTRICT修改为__RRSTRICT,程序闪退。
  13. linux man手册_Linux微操(基于Centos)
  14. java.lang.VerifyError: Expecting a stack map frame
  15. Java中的静态方法和单例模式比较
  16. Linux常用终端命令及扩展(五)
  17. x3850x5服务器内存_有图有真相 IBM System x3850 X5拆机秀
  18. springboot+vue旅行社旅游拼团系统java
  19. win10系统云服务器配置,win10系统云服务器配置
  20. 玩转人工智能(11)使用Pyspark上手机器学习

热门文章

  1. 算法:罗马数字转整数
  2. 开源推荐:可用于生产的java聚合支付系统
  3. 【平面几何】点线距离与位置关系
  4. 001云E办项目之创建项目
  5. win10产品密钥查看
  6. 移动硬盘提示“需要格式化”
  7. php 连接局域网打印机,如何添加局域网打印机?局域网打印机添加方法介绍
  8. Sklearn提供的常用数据集
  9. cron表达式每隔1小时一次_cron 每隔1小时50分钟_cron每小时执行一次
  10. mysql 误删表怎么恢复_怎么恢复navicat删除的表