如何判斷一個圖中是否存在環路


最近公司的項目中,有個樹形結構變圖結構的問題。本來我們對項目中實體之間的關系是按樹形結構來表示的,也就是說實體之間不會重用,也不會有環。現在我們需要變成圖的結構,實體之間可以重用,但不能有環。那么該如何解決這個問題呢?

我們先定義出什么是環:

環定義:從一條邊出發,如果能回到當前邊則證明有環。

沒有環的圖
可見,根據定義,上面的圖不存在環。因為從任意一條邊出發都不可能回到自身。下面給出一個帶有環的圖。
圖三
圖中紅色箭頭的3,4,5構成了一個環路。因為從3、4、5任一點出發,都可以回到起始點。那么我們如何使用代碼去判斷出圖中是否有環呢?

代碼邏輯

首先你可能想到的是,判斷節點是否被重用過。但是這個辦法行不通,第一張圖給出的就是這個思路的反例。
正確的辦法是,我們需要明確這是用圖來描述的結構,圖的定義可以參考圖數據結構
例如上面這張圖,我們可以給出這個圖的所有邊(在項目中,每條邊就是一個ResRelation):

1 -> 2
1 -> 3
1 -> 4
2 -> 4
3 -> 4
4 -> 5
5 -> 3

我們需要維持一個集合color來標記某條邊是否被訪問過,維持一個隊列queue用來進行迭代算法。當迭代遍歷整個圖的時候,首先將1(也可以是任意起始點)加入隊列,由於1的可達路徑為[2,3,4]。所有我們將1的可達路徑加入隊列。同時color中標記1 -> 2,1 ->3,1 -> 4

繼續迭代,當前queue中的節點是[2,3,4],先彈出2,找到了2的可達路徑為4,同時將2 -> 4標記為已訪問。節點3同樣的道理。

這時候queue中的值可能重復,因為[1,2,3]都可達4,代碼中我做了一個去重。因此現在queue中只有4。
尋找4的可達路徑,我們找到了5,同時標記4 -> 5。根據5尋找可達路徑,我們找到了3,同時標記5 -> 3。此時queue中只有一個節點3,再次迭代尋找3的可達路徑,我們找到了3 -> 4,這時候我們發現這個邊被訪問過了。因此這個圖中有環。代碼如圖:

    private static boolean isCircle(String id, List<ResRelation> resRelationList) {
        boolean isCircle = false;
		/* 隊列*/
        LinkedList<String> queue = Lists.newLinkedList(Lists.newArrayList(id));
		/* 標記集合*/
        Set<ResRelation> color=new HashSet<>();
        while (queue.size() > 0) {
            String parentId = queue.poll();
            for (ResRelation i : resRelationList) {
				/* 找到節點的對應的關系,也就是邊*/
                if (parentId.equals(i.getStart_id())) {
					/* 查看邊是否被訪問過*/
                    if(!color.contains(i)){
                        color.add(i);
                        if(!queue.contains(i.getEnd_id())){
							/*加入隊列 */
                            queue.add(i.getEnd_id());
                        }
                    }else{
						/* 如果重復訪問,則有環*/
                        isCircle=true;
                        queue.clear();
                        break;
                    }
                }
            }
        }
        return isCircle;
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM