Çoğunlukla DFS, BFS'de değil grafiklerde bir döngü bulmak için kullanılır. Herhangi bir sebep var mı? Her ikisi de, ağacı / grafiği dolaşırken bir düğümün daha önce ziyaret edilip edilmediğini bulabilir.
Çoğunlukla DFS, BFS'de değil grafiklerde bir döngü bulmak için kullanılır. Herhangi bir sebep var mı? Her ikisi de, ağacı / grafiği dolaşırken bir düğümün daha önce ziyaret edilip edilmediğini bulabilir.
Yanıtlar:
Önce derinlik araması, daha erken geri dönüş yapabileceğiniz için, kapsamlı ilk aramadan daha fazla bellek verimlidir. Çağrı yığınını kullanırsanız uygulanması da daha kolaydır, ancak bu, yığını aşmayan en uzun yola dayanır.
Ayrıca grafiğiniz yönlendirilmişse , sadece bir düğümü ziyaret edip etmediğinizi değil, aynı zamanda oraya nasıl geldiğinizi de hatırlamanız gerekir. Aksi takdirde bir döngü bulduğunuzu düşünebilirsiniz ama gerçekte sahip olduğunuz tek şey iki ayrı A-> B yoludur, ancak bu bir B-> A yolu olduğu anlamına gelmez. Örneğin,
BFS'yi baştan yaparsanız 0
, döngü mevcut olarak algılanır, ancak aslında döngü yoktur.
Derinlikli ilk aramayla, alçalırken düğümleri ziyaret edilmiş olarak işaretleyebilir ve geriye doğru giderken işaretlerini kaldırabilirsiniz. Bu algoritmada bir performans iyileştirmesi için yorumlara bakın.
Yönlendirilmiş bir grafikte döngüleri tespit etmek için en iyi algoritma için Tarjan'ın algoritmasına bakabilirsiniz .
Grafik yönlendirilmemişse (döngüleri yönlendirilmiş bir grafikte rapor edecek verimli bir algoritma göstermek için konuğum olun!), Her "çapraz kenarın" bir döngüyü tanımladığı bir BFS makul olabilir. Kesişen kenar ise {v1, v2}
ve bu düğümleri içeren kök (BFS ağacında) ise r
, döngü r ~ v1 - v2 ~ r
( ~
bir yoldur, -
tek bir uçtur) ve neredeyse DFS'deki kadar kolay raporlanabilir.
Bir BFS kullanmanın tek nedeni, (yönlenmemiş) grafiğinizin uzun yollara ve küçük yol kaplamasına (başka bir deyişle, derin ve dar) sahip olacağını biliyorsanız olabilir. Bu durumda, BFS kuyruğu için DFS yığınına göre orantılı olarak daha az bellek gerektirir (her ikisi de tabii ki doğrusaldır).
Diğer tüm durumlarda, DFS açıkça kazanır. Hem yönlendirilmiş hem de yönlendirilmemiş grafikler üzerinde çalışır ve döngüleri rapor etmek önemsizdir - atadan nesile giden yolun herhangi bir arka kenarını birleştirin ve döngüyü elde edersiniz. Sonuç olarak, bu sorun için BFS'den çok daha iyi ve pratik.
BFS, döngüleri bulmada yönlendirilmiş bir grafik için çalışmayacaktır. Bir grafikte A-> B ve A-> C-> B'yi A'dan B'ye yollar olarak düşünün. BFS, B'nin ziyaret edildiği yollardan birini geçtikten sonra söyleyecektir. Bir sonraki yola gitmeye devam ederken, işaretli düğüm B'nin tekrar bulunduğunu söyleyecektir, dolayısıyla orada bir döngü vardır. Açıkçası burada döngü yok.
Beslememde neden bu kadar eski bir sorunun belirdiğini bilmiyorum ama önceki cevapların hepsi kötü, bu yüzden ...
DFS, çalıştığı için yönlendirilmiş grafiklerde döngüleri bulmak için kullanılır .
DFS'de her köşe, "ziyaret edilir"; burada bir köşe ziyaretinin anlamı:
O tepeden ulaşılabilen alt grafik ziyaret edilir. Bu, o tepe noktasından erişilebilen tüm işlenmemiş kenarların izlenmesini ve tüm erişilebilir ziyaret edilmemiş tepe noktalarının ziyaret edilmesini içerir.
Köşe bitti.
Kritik özellik, bir tepe noktasından erişilebilen tüm kenarların, tepe bitmeden önce izlenmesidir. Bu, DFS'nin bir özelliğidir, ancak BFS değildir. Aslında bu, DFS'nin tanımıdır.
Bu özellik nedeniyle, bir döngüdeki ilk tepe noktası başladığında şunu biliyoruz :
Öyleyse, bir döngü varsa, o zaman başlamış ama bitmemiş bir tepe noktasına (2) bir kenar bulmamız garanti edilir ve böyle bir kenar bulursak, o zaman bir döngü (3) olduğu garanti edilir.
Bu nedenle DFS, yönlendirilmiş grafiklerde döngüleri bulmak için kullanılır.
BFS böyle bir garanti vermez, bu yüzden işe yaramaz. (Alt prosedür olarak BFS veya benzerini içeren mükemmel derecede iyi döngü bulma algoritmalarına rağmen)
Öte yandan, yönsüz bir grafik, herhangi bir tepe çifti arasında iki yol olduğunda, yani bir ağaç olmadığında bir döngüye sahiptir. Bunu BFS veya DFS sırasında tespit etmek kolaydır - Yeni tepe noktalarına izlenen kenarlar bir ağaç oluşturur ve diğer herhangi bir kenar bir döngüyü gösterir.
Bir ağaçta rastgele bir noktaya bir döngü yerleştirirseniz, DFS, ağacın yaklaşık yarısını kapladığında döngüye çarpma eğiliminde olacaktır ve zamanın yarısı, döngünün gittiği yerden geçmiş olacaktır ve zamanın yarısı geçmeyecektir ( ortalama olarak ağacın geri kalanının yarısında bulacaktır), bu nedenle ağacın ortalama 0,5 * 0,5 + 0,5 * 0,75 = 0,625'ini değerlendirecektir.
Bir ağaçta rastgele bir noktaya bir döngü yerleştirirseniz, BFS, yalnızca ağacın katmanı o derinlikte değerlendirildiğinde döngüye çarpma eğiliminde olacaktır. Bu nedenle, genellikle bir denge ikili ağacın yapraklarını değerlendirmek zorunda kalırsınız, bu da genellikle ağacın daha fazlasını değerlendirmeye neden olur. Özellikle, zamanın 3 / 4'ünde iki bağlantıdan en az biri ağacın yapraklarında görünür ve bu durumlarda ağacın ortalama 3 / 4'ünü (bir bağlantı varsa) veya 7 / Ağacın 8'i (eğer iki tane varsa), yani 1/2 * 3/4 + 1/4 * 7/8 = (7 + 12) / 32 = 21/32 = arama beklentiniz zaten var 0,656 ... yaprak düğümlerinden uzakta bir döngü eklenmiş bir ağaç arama maliyetini bile eklemeden ağaca.
Ek olarak, DFS'nin uygulanması BFS'den daha kolaydır. Dolayısıyla, döngüleriniz hakkında bir şey bilmediğiniz sürece kullanabileceğiniz tek şey budur (örneğin, çevrimler muhtemelen arama yaptığınız köke yakın olacaktır, bu noktada BFS size bir avantaj sağlar).
Bir grafiğin döngüsel olduğunu kanıtlamak için, sadece bir döngüye sahip olduğunu kanıtlamanız gerekir (kenar doğrudan veya dolaylı olarak kendisine işaret eder).
DFS'de her seferinde bir tepe noktası alırız ve döngü olup olmadığını kontrol ederiz. Bir döngü bulunur bulunmaz, diğer köşeleri kontrol etmeyi atlayabiliriz.
BFS'de birçok köşe kenarını eşzamanlı olarak takip etmemiz gerekir ve çoğu zaman sonunda döngü olup olmadığını öğrenirsiniz. Grafiğin boyutu büyüdükçe BFS, DFS'ye kıyasla daha fazla alan, hesaplama ve zaman gerektirir.
Özyinelemeli veya yinelemeli uygulamalardan bahsedip bahsetmediğinize bağlıdır.
Özyinelemeli DFS, her düğümü iki kez ziyaret eder. Yinelemeli-BFS, her düğümü bir kez ziyaret eder.
Bir döngüyü tespit etmek istiyorsanız, düğümleri hem bir düğümde "başlattığınızda" hem de bir düğümle "bitirdiğinizde" bitişiklerini eklemeden önce ve sonra araştırmanız gerekir.
Bu, Yinelemeli-BFS'de daha fazla çalışma gerektirir, bu nedenle çoğu kişi Özyinelemeli DFS'yi seçer.
Örneğin std :: stack ile Yinelemeli-DFS'nin basit bir uygulamasının Yinelemeli-BFS ile aynı soruna sahip olduğuna dikkat edin. Bu durumda, bir düğüm üzerinde çalışmayı "bitirdiğinizde" izlemek için yığına kukla öğeler yerleştirmeniz gerekir.
Yinelemeli-DFS'nin bir düğümle ne zaman "bitirdiğinizi" (TopoSort bağlamında yanıtlanır) belirlemek için nasıl ek çalışma gerektirdiğine ilişkin daha fazla ayrıntı için bu yanıta bakın:
Özyineleme olmadan DFS kullanarak topolojik sıralama
Umarım bu, bir düğümü işlemeyi "bitirdiğinizde" belirlemeniz gereken sorunlar için insanların neden Özyinelemeli DFS'yi tercih ettiğini açıklar.
BFS
Yönlendirilmiş bir grafikte belirli bir düğümü içeren en kısa döngüyü bulmak istediğinizde kullanmanız gerekecek .
Verilen düğüm 2 ise, - [2,3,4]
, [2,3,4,5,6,7,8,9]
&' nin parçası olduğu üç döngü vardır [2,5,6,7,8,9]
. En kısa[2,3,4]
Bunu BFS kullanarak uygulamak için, uygun veri yapılarını kullanarak ziyaret edilen düğümlerin geçmişini açıkça korumanız gerekir.
Ancak diğer tüm amaçlar için (örneğin: herhangi bir döngüsel yol bulmak veya bir döngünün var olup olmadığını kontrol etmek), DFS
diğerleri tarafından bahsedilen nedenlerden dolayı açık bir seçimdir.