Yönlendirilmiş bir grafiğin döngüsel olmadığını nasıl kontrol ederim?


82

Yönlendirilmiş bir grafiğin döngüsel olmadığını nasıl kontrol ederim? Ve algoritma nasıl adlandırılır? Bir referans için minnettar olurum.


SO'da yanlış yanıtları "düzeltmenin" bir yolunu destekleyen başka bir durum.
Sparr

2
Yani, ben çoğunlukla onu bulmak için gereken zamanla ilgileniyorum. Yani, sadece soyut algoritmaya ihtiyacım var.
nes1983

alt sınır O (| V | + | E |) olacak şekilde tüm kenarları geçmeli ve tüm köşeleri kontrol etmelisiniz. DFS ve BFS aynı karmaşıklığa sahiptir, ancak yığını sizin için yönettiği için özyinelemeniz varsa DFS'yi kodlamak daha kolaydır ...
ShuggyCoUk

DFS aynı karmaşıklık değildir . {1 .. N} düğümlü grafiği ve {(a, b) | biçimindeki kenarları düşünün. a <b}. Bu grafik, asiklik ve henüz DFS O olacaktır (n!)
FryGuy

1
DFS hiçbir zaman O (n!) Değildir. Her düğümü bir kez ve her kenarı en fazla iki kez ziyaret eder. Yani O (| V | + | E |) veya O (n).
Jay Conrod

Yanıtlar:


95

Grafiği topolojik olarak sıralamayı denerdim ve yapamazsanız, o zaman döngüleri vardır.


2
Bunun nasıl oy alamadı? Düğümlerde + kenarlarda doğrusaldır, O (n ^ 2) çözümlerinden çok daha üstündür!
Loren Pechtel

5
Çoğu durumda, bir DFS (bkz. J.Conrod'un cevabı), özellikle de yine de bir DFS yapılması gerekiyorsa daha kolay olabilir. Ancak elbette bu duruma göre değişir.
sleske

1
Topolojik sıralama sonsuz bir döngüde olacak, ancak bize döngünün nerede oluştuğunu söylemeyecek ...
Baradwaj Aryasomayajula

35

Basit bir derinlemesine arama yapmak, bir döngü bulmak için yeterince iyi değildir . Bir döngü mevcut olmadan bir DFS'de bir düğümü birden çok kez ziyaret etmek mümkündür. Nereden başladığınıza bağlı olarak, grafiğin tamamını da ziyaret etmeyebilirsiniz.

Bir grafiğin bağlı bir bileşenindeki döngüleri aşağıdaki gibi kontrol edebilirsiniz. Yalnızca giden kenarları olan bir düğüm bulun. Böyle bir düğüm yoksa, o zaman bir döngü vardır. Bu düğümde bir DFS başlatın. Her bir kenarı geçerken, kenarın zaten yığınızda bulunan bir düğüme işaret edip etmediğini kontrol edin. Bu bir döngünün varlığını gösterir. Böyle bir kenar bulamazsanız, o bağlı bileşende döngü yoktur.

Rutger Prins'in işaret ettiği gibi, grafiğiniz bağlı değilse, aramayı bağlı her bir bileşen üzerinde tekrarlamanız gerekir.

Referans olarak, Tarjan'ın güçlü bir şekilde bağlantılı bileşen algoritması yakından ilişkilidir. Ayrıca döngüleri bulmanıza da yardımcı olur, sadece var olup olmadıklarını bildirmez.


2
BTW: "Yığınınızdaki bir düğüme geri dönen" bir kenar, açık nedenlerden dolayı literatürde genellikle "arka kenar" olarak adlandırılır. Ve evet, bu, özellikle de yine de bir DFS yapmanız gerekiyorsa, grafiği topolojik olarak sıralamaktan daha basit olabilir.
sleske

Grafiğin döngüsel olmaması için, her bağlı bileşenin yalnızca giden kenarları olan bir düğüm içermesi gerektiğini söylüyorsunuz. Ana algoritmanız tarafından kullanılmak üzere yönlendirilmiş bir grafiğin bağlı bileşenlerini ("güçlü" bağlı bileşenlerin aksine) bulmak için bir algoritma önerebilir misiniz?
kostmo

@kostmo, grafiğin birden fazla bağlı bileşeni varsa, ilk DFS'nizdeki tüm düğümleri ziyaret etmeyeceksiniz. Ziyaret ettiğiniz düğümleri takip edin ve hepsine ulaşana kadar algoritmayı ziyaret edilmemiş düğümlerle tekrarlayın. Bu, temelde bağlantılı bir bileşen algoritmasının nasıl çalıştığıdır.
Jay Conrod

6
Bu yanıtın amacı doğru olsa da, yığın tabanlı bir DFS uygulaması kullanılıyorsa, yanıt kafa karıştırıcıdır: DFS'yi uygulamak için kullanılan yığın, test edilecek doğru öğeleri içermeyecektir. Üst düğüm kümesini takip etmek için kullanılan algoritmaya ek bir yığın eklemek gerekir.
Theodore Murdock

Cevabınızla ilgili çok sayıda sorum var. Onları buraya gönderdim: stackoverflow.com/questions/37582599/…
Ari

14

Kitapta Introduction to Algorithms(İkinci Baskı) Lemma 22.11 şunu belirtir:

Yönlendirilmiş bir G grafiği, ancak ve ancak önce derinlik araması G'nin hiçbir arka kenar vermemesi durumunda çevrimsizdir


1
Bu temelde Jay Conrod'un cevabının kısaltılmış bir versiyonudur :-).
sleske

Aynı kitaptaki sorunlardan biri bir | V | zaman algoritması. Burada yanıtlanıyor: stackoverflow.com/questions/526331/…
Justin

9

Çözüm 1Döngüyü kontrol etmek için Kahn algoritması . Ana fikir: Derecesi sıfır olan düğümün kuyruğa ekleneceği bir kuyruk oluşturun. Ardından sıra boşalana kadar düğümü tek tek ayırın. Herhangi bir düğümün iç kenarlarının olup olmadığını kontrol edin.

Solution2 : Tarjan algoritması güçlü bağlı bileşen kontrol etmek için.

3. Çözüm : DFS . Düğümün mevcut durumunu etiketlemek için tamsayı dizisi kullanın: yani 0 - bu düğümün daha önce ziyaret edilmediği anlamına gelir. -1 - bu düğümün ziyaret edildiği ve alt düğümlerinin ziyaret edildiği anlamına gelir. 1 - bu düğümün ziyaret edildiği ve tamamlandığı anlamına gelir. Dolayısıyla, DFS yaparken bir düğümün durumu -1 ise, bu, bir döngü olması gerektiği anlamına gelir.


2

ShuggyCoUk tarafından verilen çözüm eksiktir çünkü tüm düğümleri kontrol etmeyebilir.


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

Bunun zaman karmaşıklığı O (n + m) veya O (n ^ 2)


benimki gerçekten yanlış - yine de sildim, bu yüzden
seninki

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

@Artru O (n ^ 2) bitişik matrisi kullanırken, O (n + m) grafiği temsil etmek için bitişiklik listesi kullanırken.
0x450

Um ... m = O(n^2)çünkü grafiğin tamamı tam olarak m=n^2kenarlara sahip. Yani bu O(n+m) = O(n + n^2) = O(n^2).
Alex Reinking

1

Bunun eski bir konu olduğunu biliyorum, ancak gelecekteki araştırmacılar için burada oluşturduğum bir C # uygulaması var (bunun en verimli olduğu iddiası yok!). Bu, her bir düğümü tanımlamak için basit bir tam sayı kullanmak üzere tasarlanmıştır. Düğüm nesnenizin sağlam ve eşit olması koşuluyla, istediğiniz gibi dekore edebilirsiniz.

Çok derin grafikler için, her bir düğümde derinlemesine bir hashset oluşturduğundan (bunlar enine boyuna yok edilir) yüksek ek yüke sahip olabilir.

Aramak istediğiniz düğümü girersiniz ve bu düğüme giden yol alırsınız.

  • Tek kök düğümü olan bir grafik için bu düğümü ve boş bir hashset gönderirsiniz
  • Birden fazla kök düğümü olan bir grafik için, bunu bu düğümlerin üzerine bir foreach olarak sarın ve her yineleme için yeni bir boş hashset geçirin.
  • Herhangi bir düğümün altındaki döngüleri kontrol ederken, o düğümü boş bir hashset ile birlikte geçirmeniz yeterlidir.

    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 yaparken herhangi bir arka kenar olmamalıdır. DFS yaparken zaten ziyaret edilen düğümleri takip edin, mevcut düğüm ile mevcut düğüm arasında bir uçla karşılaşırsanız, o zaman grafiğin döngüsü vardır.


1

bir grafiğin döngüleri olup olmadığını bulmak için hızlı bir kod:

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)

Fikir şuna benzer: Ziyaret edilen düğümleri takip etmek için bir diziye sahip normal bir dfs algoritması ve mevcut düğüme götüren düğümler için bir işaretçi olarak hizmet eden ek bir dizi, böylece bir düğüm için bir dfs çalıştırdığımızda buna karşılık gelen öğeyi işaretçi dizisindeki doğru olarak ayarladık, böylece önceden ziyaret edilmiş bir düğümle karşılaşıldığında, işaretçi dizisindeki karşılık gelen öğesinin doğru olup olmadığını kontrol ederiz, eğer doğruysa, kendisine izin veren düğümlerden biridir (dolayısıyla bir döngü) ve işin püf noktası, bir düğümün df'leri geri döndüğünde, karşılık gelen işaretçisini yanlış olarak ayarlıyoruz, böylece onu başka bir rotadan tekrar ziyaret edersek kandırılmayız.


1

Bu soruyu bir Google röportajında ​​aldım.

Topolojik Sıralama

Topolojik olarak sıralamayı deneyebilirsiniz, bu O (V + E) V köşe sayısı ve E kenar sayısıdır. Yönlendirilmiş bir grafik, ancak ve ancak bu yapılabilirse döngüsel değildir.

Yinelemeli Yaprak Çıkarma

Yaprak düğümleri, hiç kalmayana kadar özyinelemeli olarak kaldırın ve tek bir düğümden fazlası kaldıysa, bir döngünüz olur. Yanılmıyorsam, bu O (V ^ 2 + VE).

DFS tarzı ~ O (n + m)

Bununla birlikte, verimli bir DFS-esque algoritması, en kötü durum 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

Yaprak düğümü soyma algoritmasının ruby uygulamam burada .

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.