यदि एक निर्देशित ग्राफ एसाइक्लिक है, तो मैं कैसे जांच सकता हूं?


82

यदि एक निर्देशित ग्राफ एसाइक्लिक है, तो मैं कैसे जांच सकता हूं? और एल्गोरिथ्म को कैसे कहा जाता है? मैं एक संदर्भ की सराहना करूंगा।


एसओ पर गलत उत्तरों को "ठीक" करने के लिए किसी तरह से एक और मामला।
२३:३३ पर

2
इसलिए, उम्म, मुझे इसे खोजने के लिए आवश्यक समय में दिलचस्पी है। तो, मुझे सिर्फ अमूर्त एल्गोरिदम की आवश्यकता है।
nes1983

आपको सभी किनारों को पार करना होगा और सभी छोरों की जांच करनी होगी ताकि निचली सीमा O (| V | + | E |) है। DFS और BFS दोनों एक ही जटिलता वाले हैं लेकिन DFS को कोड करना आसान है अगर आपके पास रिकर्सन है जैसा कि आपके लिए स्टैक का प्रबंधन करता है ...
ShuggyCoUk

डीएफएस एक ही जटिलता नहीं है। नोड्स {1 .. एन} के साथ ग्राफ पर विचार करें, और फॉर्म {(ए, बी) में किनारों | ए <बी}। यह ग्राफ एसाइक्लिक है, और फिर भी DFS O (n!) होगा
FryGuy

1
DFS कभी O (n!) नहीं है। यह प्रत्येक नोड पर एक बार और प्रत्येक किनारे पर अधिकतम दो बार जाता है। अतः O (| V | + | E |) या O (n)।
जे कॉनरोड

जवाबों:


95

मैं ग्राफ़ को स्थैतिक रूप से क्रमबद्ध करने की कोशिश करूंगा , और यदि आप नहीं कर सकते हैं, तो इसमें चक्र हैं।


2
यह कैसे कोई वोट नहीं था ?? यह नोड्स + किनारों पर रैखिक है, O (n ^ 2) समाधानों से बहुत बेहतर है!
लोरेन Pechtel

5
कई मामलों में, एक डीएफएस (जे.कॉनरॉड का जवाब देखें) आसान हो सकता है, खासकर अगर किसी डीएफएस को वैसे भी प्रदर्शन करने की आवश्यकता होती है। लेकिन निश्चित रूप से यह संदर्भ पर निर्भर करता है।
सेल्के

1
सामयिक आदेश एक अनंत लूप में होगा लेकिन यह हमें यह नहीं कहेगा कि चक्र कहां होता है ...
बाराद्वज आर्यसोमयजुला

35

एक सरल गहराई-पहली-खोज करना एक चक्र खोजने के लिए पर्याप्त नहीं है। मौजूदा चक्र के बिना डीएफएस में कई बार नोड का दौरा करना संभव है। आप कहाँ से शुरू करते हैं, इस पर निर्भर करते हुए, आप पूरे ग्राफ़ पर भी नहीं जा सकते हैं।

आप ग्राफ के जुड़े घटक में निम्नानुसार चक्रों की जांच कर सकते हैं। एक नोड खोजें, जिसमें केवल आउटगोइंग किनारे हों। यदि ऐसा कोई नोड नहीं है, तो एक चक्र है। उस नोड पर DFS प्रारंभ करें। प्रत्येक किनारे को ट्रेस करते समय, जांचें कि क्या किनारे पहले से ही आपके स्टैक पर एक नोड को वापस इंगित करता है। यह एक चक्र के अस्तित्व को इंगित करता है। यदि आपको ऐसा कोई किनारा नहीं मिलता है, तो उस जुड़े घटक में कोई चक्र नहीं हैं।

जैसा कि रटर प्रिन्स बताते हैं, यदि आपका ग्राफ जुड़ा नहीं है, तो आपको प्रत्येक जुड़े घटक पर खोज को दोहराना होगा।

एक संदर्भ के रूप में, टारजन का दृढ़ता से जुड़ा घटक एल्गोरिथ्म बारीकी से संबंधित है। यह आपको चक्रों को खोजने में भी मदद करेगा, न कि केवल रिपोर्ट करें कि क्या वे मौजूद हैं।


2
BTW: एक किनारा जो "आपके स्टैक पर पहले से ही नोड पर वापस इंगित करता है" अक्सर स्पष्ट कारणों से साहित्य में "बैक एज" कहा जाता है। और हाँ, यह ग्राफ को स्थैतिक रूप से छांटने से अधिक सरल हो सकता है, खासकर यदि आपको किसी भी तरह डीएफएस करने की आवश्यकता हो।
सेल्के

ग्राफ के लिए चक्रीय होने के लिए, आप कहते हैं कि प्रत्येक जुड़े घटक में केवल निवर्तमान किनारों के साथ एक नोड होना चाहिए। क्या आप अपने मुख्य एल्गोरिथ्म द्वारा उपयोग के लिए, एक निर्देशित ग्राफ के जुड़े घटकों ("दृढ़ता से" जुड़े घटकों के विपरीत) को खोजने के लिए एक एल्गोरिदम की सिफारिश कर सकते हैं?
कोस्टो

@kostmo, यदि ग्राफ़ में एक से अधिक जुड़े हुए घटक हैं, तो आप अपने पहले DFS के सभी नोड्स पर नहीं जाएंगे। आपके द्वारा देखे गए नोड्स का ध्यान रखें, और जब तक आप उन सभी तक नहीं पहुंचते तब तक एल्गोरिदम को गैर-स्वीकृत नोड्स के साथ दोहराएं। यह मूल रूप से एक जुड़ा हुआ घटक एल्गोरिदम कैसे भी काम करता है।
जे कॉनरोड

6
हालांकि इस उत्तर की मंशा सही है, डीएफएस के स्टैक-आधारित कार्यान्वयन का उपयोग करने पर उत्तर भ्रमित कर रहा है: डीएफएस को लागू करने के लिए उपयोग किए गए स्टैक में परीक्षण करने के लिए सही तत्व नहीं होंगे। पूर्वज नोड्स के सेट का ट्रैक रखने के लिए उपयोग किए जाने वाले एल्गोरिदम में एक अतिरिक्त स्टैक जोड़ना आवश्यक है।
थियोडोर मर्डॉक

मेरे पास आपके उत्तर के बारे में कई प्रश्न हैं। मैंने उन्हें यहाँ पोस्ट किया: stackoverflow.com/questions/37582599/…
एर

14

पुस्तक पर लेम्मा 22.11 Introduction to Algorithms(दूसरा संस्करण) बताता है कि:

एक निर्देशित ग्राफ जी एसाइक्लिक है अगर और केवल अगर जी की गहराई-पहली खोज से कोई बैक एज नहीं निकलता है


1
यह मूल रूप से जय कॉनरोड के उत्तर :-) का संक्षिप्त रूप है।
15

एक ही पुस्तक की समस्याओं में से एक का सुझाव लगता है वहाँ एक है | V | V | समय एल्गोरिथ्म। इसका उत्तर यहाँ दिया गया है: stackoverflow.com/questions/526331/…
जस्टिन

9

समाधान 1 1 काहन एल्गोरिदम चक्र की जाँच करने के लिए । मुख्य विचार: एक कतार बनाए रखें जहाँ शून्य इन-डिग्री वाले नोड को कतार में जोड़ा जाएगा। फिर नोड को एक-एक करके छीलें जब तक कि कतार खाली न हो जाए। जांचें कि क्या किसी नोड के किनारों में मौजूद हैं।

Solution2 : मजबूत जुड़े घटक की जाँच करने के लिए टारजन एल्गोरिदम

Solution3 : DFS । नोड की वर्तमान स्थिति को टैग करने के लिए पूर्णांक सरणी का उपयोग करें: यानी 0 --means यह नोड पहले नहीं देखा गया है। -1 - इसका मतलब है कि इस नोड का दौरा किया गया है, और इसके बच्चों के नोड्स का दौरा किया जा रहा है। 1 - इसका मतलब है कि इस नोड का दौरा किया गया है, और यह हो चुका है। इसलिए यदि DFS करते समय एक नोड की स्थिति -1 है, तो इसका मतलब है कि एक चक्र मौजूद होना चाहिए।


2

ShuggyCoUk द्वारा दिया गया समाधान अधूरा है क्योंकि यह सभी नोड्स की जाँच नहीं कर सकता है।


def isDAG(nodes V):
    while there is an unvisited node v in V:
        bool cycleFound = dfs(v)
        if cyclefound:
            return false
    return true

यह समयबद्धता O (n + m) या O (n ^ 2) है


मेरा वास्तव में गलत है - मैंने इसे डिलीट कर दिया है, लेकिन अब आपके संदर्भ से थोड़ा बाहर लगता है
ShuggyCoUk

3
O (n + m) <= O (n + n) = O (2n), O (2n)! = O (n ^ 2)
Artru

@Artru O (n ^ 2) आसन्न मैट्रिक्स का उपयोग करते समय, ग्राफ़ का प्रतिनिधित्व करने के लिए आसन्न सूची का उपयोग करते समय O (n + m)।
0x450

उम ... m = O(n^2)चूंकि पूर्ण ग्राफ में बिल्कुल m=n^2किनारे हैं। तो वह है O(n+m) = O(n + n^2) = O(n^2)
एलेक्स रिंकिंग

1

मुझे पता है कि यह एक पुराना विषय है लेकिन भविष्य के खोजकर्ताओं के लिए यहाँ एक C # कार्यान्वयन है जिसे मैंने बनाया (कोई दावा नहीं कि यह सबसे कुशल है)। यह प्रत्येक नोड की पहचान करने के लिए एक साधारण पूर्णांक का उपयोग करने के लिए डिज़ाइन किया गया है। आप अपने नोड ऑब्जेक्ट हैश प्रदान की है और ठीक से बराबरी की तरह है कि हालांकि आप इसे सजाने कर सकते हैं।

बहुत गहरे रेखांकन के लिए यह उच्च ओवरहेड हो सकता है, क्योंकि यह गहराई से प्रत्येक नोड पर एक हैशसेट बनाता है (वे चौड़ाई से अधिक नष्ट हो जाते हैं)।

आप उस नोड को इनपुट करते हैं जिससे आप खोजना चाहते हैं और पथ उस नोड पर ले जाता है।

  • एक एकल रूट नोड के साथ एक ग्राफ के लिए आप उस नोड और एक खाली हैशसेट को भेजते हैं
  • कई रूट नोड वाले ग्राफ़ के लिए आप इसे उन नोड्स पर फ़ॉर्च में लपेटते हैं और प्रत्येक पुनरावृत्ति के लिए एक नया खाली हैशसेट पास करते हैं
  • किसी भी नोड के नीचे चक्रों की जांच करते समय, बस उस नोड को खाली हैशसेट के साथ पास करें

    private bool FindCycle(int node, HashSet<int> path)
    {
    
        if (path.Contains(node))
            return true;
    
        var extendedPath = new HashSet<int>(path) {node};
    
        foreach (var child in GetChildren(node))
        {
            if (FindCycle(child, extendedPath))
                return true;
        }
    
        return false;
    }
    

1

DFS करते समय कोई बैक एज नहीं होना चाहिए। DFS करते समय पहले से देखे गए नोड्स का ट्रैक देखें, यदि आप वर्तमान नोड और मौजूदा नोड के बीच बढ़त का सामना करते हैं, तो ग्राफ में चक्र है।


1

यदि ग्राफ़ में चक्र है, तो यह जानने के लिए एक तेज़ कोड है:

func isCyclic(G : Dictionary<Int,Array<Int>>,root : Int , var visited : Array<Bool>,var breadCrumb : Array<Bool>)-> Bool
{

    if(breadCrumb[root] == true)
    {
        return true;
    }

    if(visited[root] == true)
    {
        return false;
    }

    visited[root] = true;

    breadCrumb[root] = true;

    if(G[root] != nil)
    {
        for child : Int in G[root]!
        {
            if(isCyclic(G,root : child,visited : visited,breadCrumb : breadCrumb))
            {
                return true;
            }
        }
    }

    breadCrumb[root] = false;
    return false;
}


let G = [0:[1,2,3],1:[4,5,6],2:[3,7,6],3:[5,7,8],5:[2]];

var visited = [false,false,false,false,false,false,false,false,false];
var breadCrumb = [false,false,false,false,false,false,false,false,false];




var isthereCycles = isCyclic(G,root : 0, visited : visited, breadCrumb : breadCrumb)

विचार इस तरह है: एक सामान्य dfs एल्गोरिथ्म के साथ एक सरणी के साथ विज़िट किए गए नोड्स का ट्रैक रखने के लिए, और एक अतिरिक्त सरणी जो नोड्स के लिए एक मार्कर के रूप में कार्य करता है जो वर्तमान नोड के लिए नेतृत्व करता है, ताकि जब हम कभी भी नोड के लिए dfs निष्पादित करें। हम मार्कर के सरणी में इसके संबंधित आइटम को सही के रूप में सेट करते हैं, ताकि जब कभी पहले से विज़िट किए गए नोड का सामना करना पड़े, तो हम जांच लें कि मार्कर सरणी में इसका संबंधित आइटम सही है या नहीं, यदि इसका सही है तो इसकी नोड्स में से एक जो खुद को देना है (इसलिए चक्र), और चाल यह है कि जब भी एक नोड का dfs वापस आता है तो हम इसके संबंधित मार्कर को गलत पर सेट करते हैं, ताकि यदि हम इसे फिर से किसी अन्य मार्ग से देखें तो हम मूर्ख न बनें।


1

Google इंटरव्यू में बस यही सवाल था।

सामयिक क्रमबद्ध

आप टोपोलॉजिकल रूप से सॉर्ट करने का प्रयास कर सकते हैं, जो कि O (V + E) है जहां V वर्टिकल की संख्या है, और E किनारों की संख्या है। एक निर्देशित ग्राफ एसाइक्लिक है यदि और केवल यदि यह किया जा सकता है।

पुनरावर्ती पत्ती निकालना

पुनरावर्ती पत्ती नोड्स को तब तक हटा दें जब तक कि कोई भी न बचा हो, और यदि आपके पास एक चक्र से अधिक एकल नोड बचा है, तो। जब तक मैं गलत नहीं हूँ, यह O (V ^ 2 + VE) है।

DFS- शैली ~ O (n + m)

हालांकि, एक कुशल DFS-esque एल्गोरिथम, सबसे खराब स्थिति O (V + E) है:

function isAcyclic (root) {
    const previous = new Set();

    function DFS (node) {
        previous.add(node);

        let isAcyclic = true;
        for (let child of children) {
            if (previous.has(node) || DFS(child)) {
                isAcyclic = false;
                break;
            }
        }

        previous.delete(node);

        return isAcyclic;
    }

    return DFS(root);
}

0

यहाँ छील बंद पत्ती नोड एल्गोरिथ्म का मेरा रूबी कार्यान्वयन है

def detect_cycles(initial_graph, number_of_iterations=-1)
    # If we keep peeling off leaf nodes, one of two things will happen
    # A) We will eventually peel off all nodes: The graph is acyclic.
    # B) We will get to a point where there is no leaf, yet the graph is not empty: The graph is cyclic.
    graph = initial_graph
    iteration = 0
    loop do
        iteration += 1
        if number_of_iterations > 0 && iteration > number_of_iterations
            raise "prevented infinite loop"
        end

        if graph.nodes.empty?
            #puts "the graph is without cycles"
            return false
        end

        leaf_nodes = graph.nodes.select { |node| node.leaving_edges.empty? }

        if leaf_nodes.empty?
            #puts "the graph contain cycles"
            return true
        end

        nodes2 = graph.nodes.reject { |node| leaf_nodes.member?(node) }
        edges2 = graph.edges.reject { |edge| leaf_nodes.member?(edge.destination) }
        graph = Graph.new(nodes2, edges2)
    end
    raise "should not happen"
end

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.