複雜度爲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

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