复杂度为O(n)的环路分离算法

1. 问题描述

给定一组有向图的路径子集,子集中不存在重复的边,且每条边的起点和终点只出现一次,且只有一条以起点的边和一条以终点的边。寻找子集中是否存在环路,并将环路和通路输出。

2. 问题解析

根据以上描述,可以知道可分为一下情况:
这里写图片描述

3. 算法设计

1. 遍历填表(O(n2)


创建保存节点的一维数组sort_set,用以保存顺序相连的节点(也可以是边)
* 把起点发在sort_set的第一个元素位置,并将当前节点设为起点;
* 以当前节点为第一个节点开始,寻找所在的边,并将此边的终点放在sort_set中,并将当前节点设为此边的终点;
* 重复以上步骤,直到不存在以当前节点为起点的边;
* 判断是否遍历完子集中所有的边,如完成,结束;反之,寻找一个未遍历过得点作为起点,重复以上步骤,直到有重复节点出现;
* 重复上一步,直到程序结束。


得到的sort_set是已经排序的子集,然后遍历子集中的点可以找出环路:
* 存在起点和终点的是通路;
* 两个相同节点之间的子集是环路。

2. 链表分离法O(n)

以上算法可以实现环路和通路的分离,但是时间复杂度为O(n2) ,当改变数据结构为链表时可以将时间复杂度变为O(n) .
其思路是:
* 创建足够数量的链表数组
std::list<int>list_ptr[MAX_NODE_NUM];
* 每条有向边都含有起始节点和终止节点,遍历子集中所有的边,检查边的起始节点和终止节点,得到一下情况:
* 边的起始节点和终止节点在列表数组中不存在,创建新的链表,把边的起始节点和终止节点放在新建的链表中;
* 边的起始节点属于链表数组中的一个链表,终止节点不存在链表数组中,把边的终止节点放在此链表的尾部;
* 边的终止节点属于链表数组中的一个链表,起始节点不存在链表数组中,把边的终止节点放在此链表的头部;
* 边的起始节点和终止节点存在于同一个链表中,构成环路,保存此环路,并清空此链表;
* 最后非空的链表就是包含起始节点和终止节点的通路,输出即可.

技巧

  • 定义一个大小为节点数量的数组,用于保存节点对应的链表索引,每次操作需要对数组进行维护,每次给定一个节点,可以通过查表的方式快速定位。
  • 通过观察可以知道,每次向链表中插入节点时,其位置一定在头部或尾部,不需要额外的操作,新建链表除外。

list

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章