Belirli bir düğüme / düğümünden yönlendirilmiş bir grafikteki TÜM döngüleri nasıl bulabilirim (yineleyebilirim)?
Örneğin, böyle bir şey istiyorum:
A->B->A
A->B->C->A
ama değil: B-> C-> B
Belirli bir düğüme / düğümünden yönlendirilmiş bir grafikteki TÜM döngüleri nasıl bulabilirim (yineleyebilirim)?
Örneğin, böyle bir şey istiyorum:
A->B->A
A->B->C->A
ama değil: B-> C-> B
Yanıtlar:
Bu sayfayı aramamda buldum ve döngüler güçlü bir şekilde bağlı bileşenlerle aynı olmadığından, aramaya devam ettim ve son olarak, yönlendirilmiş bir grafiğin tüm (temel) döngülerini listeleyen verimli bir algoritma buldum. Donald B. Johnson'dan ve makale aşağıdaki bağlantıda bulunabilir:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Java uygulaması şu adreste bulunabilir:
http://normalisiert.de/code/java/elementaryCycles.zip
Johnson algoritmasının bir Mathematica gösterimi burada bulunabilir , uygulama sağdan indirilebilir ( "Yazar kodunu indir" ).
Not: Aslında, bu sorun için birçok algoritma vardır. Bazıları bu makalede listelenmiştir:
http://dx.doi.org/10.1137/0205007
Makaleye göre, Johnson algoritması en hızlı olanıdır.
A->B->C->A
temel de değil mi?
simple_cycle
networkx'teki gibi uygulanır.
Geri izleme ile derinlik ilk araması burada çalışmalıdır. Daha önce bir düğümü ziyaret edip etmediğinizi takip etmek için bir dizi boolean değer tutun. Gitmek için yeni düğümleriniz biterse (daha önce bulunduğunuz bir düğüme çarpmadan), sadece geri dönüp farklı bir dal deneyin.
Grafiği temsil eden bir bitişiklik listeniz varsa DFS'nin uygulanması kolaydır. Örneğin adj [A] = {B, C}, B ve C'nin A'nın çocukları olduğunu gösterir.
Örneğin, aşağıdaki sözde kod. "start", başladığınız düğümdür.
dfs(adj,node,visited):
if (visited[node]):
if (node == start):
"found a path"
return;
visited[node]=YES;
for child in adj[node]:
dfs(adj,child,visited)
visited[node]=NO;
Yukarıdaki işlevi başlangıç düğümü ile çağırın:
visited = {}
dfs(adj,start,visited)
if (node == start):
- node and start
ilk çağrıda ne var
start
). Bu tepe noktasında başlar ve tekrar bu tepe noktasına geri dönene kadar bir DFS yapar, sonra bir döngü bulduğunu bilir. Ama aslında döngülerin çıktısını almaz, sadece bir sayıdır (ancak bunu yapmak için değiştirmek çok zor olmamalıdır).
start
. Her ziyaret edilen bayrak nedeniyle silinecek olduğundan, ziyaret edilen bayrakları temizlemenize gerek yoktur visited[node]=NO;
. Ancak, bir döngünüz varsa A->B->C->A
, bunların 3'ünde olduğu gibi 3 kez tespit edeceğinizi unutmayın start
. Bunu önlemek için bir fikir, bir noktada düğüm olan her düğümün start
ayarlandığı ve daha sonra bunları tekrar ziyaret etmediğiniz başka bir ziyaret edilen diziye sahip olmaktır .
Her şeyden önce - gerçekten tüm döngüleri bulmayı denemek istemezsiniz çünkü 1 varsa, bunlardan sonsuz sayıda vardır. Örneğin ABA, ABABA vb. Veya 2 döngüyü 8 benzeri bir döngüye vb. Birleştirmek mümkün olabilir. başlangıç / bitiş noktasında. Daha sonra isterseniz basit döngülerin kombinasyonlarını oluşturabilirsiniz.
Yönlendirilmiş bir grafikteki tüm basit döngüleri bulmak için temel algoritmalardan biri şudur: Grafikteki tüm basit yolların (kendiliğinden geçmeyenler) derinlik-ilk geçişini yapın. Geçerli düğümün yığın üzerinde ardılı olduğunda, basit bir döngü keşfedilir. Tanımlanan halefle başlayan ve yığının tepesi ile biten destedeki elemanlardan oluşur. Tüm basit yolların derinlikteki ilk geçişi, önce derinlik aramasına benzer, ancak o anda yığınta bulunanlar dışındaki ziyaret edilen düğümleri durma noktası olarak işaretlemez / kaydetmezsiniz.
Yukarıdaki kaba kuvvet algoritması korkunç derecede verimsizdir ve buna ek olarak döngülerin birden fazla kopyasını oluşturur. Bununla birlikte, performansı artırmak ve döngü çoğaltmasını önlemek için çeşitli geliştirmeler uygulayan çoklu pratik algoritmaların başlangıç noktasıdır. Bir süre önce bu algoritmaların ders kitaplarında ve web'de mevcut olmadığını öğrenmek beni şaşırttı. Bu yüzden bir araştırma yaptım ve burada açık kaynaklı bir Java kütüphanesinde yönlendirilmemiş grafiklerde döngüler için 4 tane algoritma ve 1 algoritma uyguladım: http://code.google.com/p/niographs/ .
BTW, yönlendirilmemiş grafiklerden bahsettiğimden beri: Bunların algoritması farklı. Yayılan bir ağaç oluşturun ve sonra ağacın bir parçası olmayan her kenar, ağaçtaki bazı kenarlarla birlikte basit bir döngü oluşturur. Bu şekilde bulunan döngüler, bir döngü tabanı oluşturur. Tüm basit döngüler daha sonra 2 veya daha fazla farklı temel döngüyü birleştirerek bulunabilir. Daha fazla ayrıntı için bkz . Bu: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf .
jgrapht
İçinde kullanılanıhttp://code.google.com/p/niographs/
örnek olarak github.com/jgrapht/jgrapht/wiki/DirectedGraphDemo
Bu sorunu çözmek için bulduğum en basit seçim, denilen python lib'i kullanmaktı. networkx
.
Bu sorunun en iyi cevabında belirtilen Johnson algoritmasını uygular, ancak yürütülmesi oldukça basittir.
Kısacası aşağıdakilere ihtiyacınız var:
import networkx as nx
import matplotlib.pyplot as plt
# Create Directed Graph
G=nx.DiGraph()
# Add a list of nodes:
G.add_nodes_from(["a","b","c","d","e"])
# Add a list of edges:
G.add_edges_from([("a","b"),("b","c"), ("c","a"), ("b","d"), ("d","e"), ("e","a")])
#Return a list of cycles described as a list o nodes
list(nx.simple_cycles(G))
Cevap: [['a', 'b', 'd', 'e'], ['a', 'b', 'c']]
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
Netleştirmek için:
Güçlü Bağlantılı Bileşenler, grafikte olası tüm döngülerde değil, içinde en az bir döngüye sahip tüm altgrafları bulur. Örneğin, güçlü bir şekilde bağlı tüm bileşenleri alıp her birini tek bir düğüme (yani, bileşen başına bir düğüm) daraltır / gruplandırır / birleştirirseniz, döngüsüz bir ağaç alırsınız (aslında bir DAG). Her bileşen (temel olarak içinde en az bir döngü bulunan bir alt grafiktir) dahili olarak daha fazla olası döngü içerebilir, bu nedenle SCC tüm olası döngüleri BULMAZ, en az bir döngüye sahip tüm olası grupları bulur ve grafikte döngüler olmayacaktır.
bir grafikteki tüm basit döngüleri bulmak için , diğerleri de belirtildiği gibi, Johnson algoritması bir adaydır.
Bunu bir kez röportaj sorusu olarak aldım, bunun sana geldiğinden şüpheleniyorsun ve yardım için buraya geleceksin. Sorunu üç soruya bölün ve bu daha kolay hale gelir.
Sorun 1) Rota sonuçlarını yinelemenin bir yolunu sağlamak için yineleyici desenini kullanın. Bir sonraki rotayı almak için mantığı koymak için iyi bir yer muhtemelen yineleyicinizin "moveNext" tir. Geçerli bir rota bulmak için veri yapınıza bağlıdır. Benim için geçerli rota olasılıkları dolu bir sql tablo oldu, bu yüzden bir kaynak verilen geçerli hedefleri almak için bir sorgu oluşturmak zorunda kaldı.
Problem 2) Onları alırken bir koleksiyona bulduğunuz her bir düğümü itin, bu, anında oluşturduğunuz koleksiyonu sorgulayarak bir nokta üzerinde çok kolay "iki katına çıkıp çıkmadığınızı" görebileceğiniz anlamına gelir.
Problem 3) Herhangi bir noktada iki katına çıktığını görürseniz, koleksiyondan bir şeyler çıkarabilir ve "yedekleyebilirsiniz". Sonra bu noktadan sonra tekrar "ilerlemeye" çalışın.
Hack: Sql Server 2008 kullanıyorsanız, verilerinizi bir ağaçta yapılandırırsanız, bunu hızlı bir şekilde çözmek için kullanabileceğiniz bazı yeni "hiyerarşi" şeyler vardır.
Arka kenarları olan DFS tabanlı varyantlar gerçekten döngüleri bulacaktır, ancak çoğu durumda minimum DEĞİLDİR döngüler . Genel olarak DFS size bir döngü olduğunu söyler ama döngüleri bulmak için yeterince iyi değildir. Örneğin, iki kenarı paylaşan 5 farklı döngü düşünün. Sadece DFS (geri izleme varyantları dahil) kullanarak döngüleri tanımlamanın basit bir yolu yoktur.
Johnson'un algoritması gerçekten de tüm benzersiz basit döngüleri verir ve iyi zaman ve alan karmaşıklığına sahiptir.
Ancak sadece MINIMAL döngüler bulmak istiyorsanız (yani herhangi bir tepe noktasından daha fazla bir döngü olabilir ve minimum döngüleri bulmak istiyoruz) VE grafiğiniz çok büyük değilse, aşağıdaki basit yöntemi kullanmayı deneyebilirsiniz. Johnson göre ÇOK basit ama oldukça yavaş.
Yani, biri kesinlikle MİNİMAL döngülerini bulmanın en kolay yolu bitişiklik matrisi kullanan tüm köşeler arasında asgari yolları bulmak için Floyd'un algoritmasını kullanmaktır. Bu algoritma Johnson kadar ideal değildir, ancak o kadar basittir ve iç halkası o kadar sıkıdır ki daha küçük grafikler için (<= 50-100 düğüm) kesinlikle kullanmak mantıklıdır. Zaman karmaşıklığı O (n ^ 3), ebeveyn izleme kullanıyorsanız alan karmaşıklığı O (n ^ 2) ve kullanmıyorsanız O (1). Her şeyden önce, bir döngü olup olmadığı sorusuna cevap bulalım. Algoritma son derece basittir. Scala'da snippet aşağıdadır.
val NO_EDGE = Integer.MAX_VALUE / 2
def shortestPath(weights: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
weights(i)(j) = throughK
}
}
}
Başlangıçta bu algoritma, tüm düğüm çiftleri arasındaki en kısa yolları bulmak için ağırlıklı kenar grafiğinde çalışır (dolayısıyla ağırlıklar argümanı). Doğru çalışması için düğümler arasında yönlendirilmiş bir kenar varsa 1 veya aksi takdirde NO_EDGE sağlamanız gerekir. Algoritma yürütüldükten sonra, NO_EDGE değerinden daha düşük değerler varsa bu düğümün değere eşit bir uzunluk döngüsüne katıldığından ana diyagonali kontrol edebilirsiniz. Aynı döngünün diğer tüm düğümleri aynı değere sahip olacaktır (ana diyagonalde).
Döngüyü yeniden yapılandırmak için, üst izleme ile algoritmanın biraz değiştirilmiş sürümünü kullanmamız gerekir.
def shortestPath(weights: Array[Array[Int]], parents: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
parents(i)(j) = k
weights(i)(j) = throughK
}
}
}
Ana noktalar matrisi başlangıçta, köşeler arasında bir kenar ve -1 ise bir kenar hücresinde kaynak tepe noktası dizini içermelidir. İşlev döndükten sonra, her kenar için en kısa yol ağacındaki üst düğüme başvuruda bulunacaksınız. Ve sonra gerçek döngüleri kurtarmak kolaydır.
Sonuçta, tüm minimum döngüleri bulmak için aşağıdaki programa sahibiz
val NO_EDGE = Integer.MAX_VALUE / 2;
def shortestPathWithParentTracking(
weights: Array[Array[Int]],
parents: Array[Array[Int]]) = {
for (k <- weights.indices;
i <- weights.indices;
j <- weights.indices) {
val throughK = weights(i)(k) + weights(k)(j)
if (throughK < weights(i)(j)) {
parents(i)(j) = parents(i)(k)
weights(i)(j) = throughK
}
}
}
def recoverCycles(
cycleNodes: Seq[Int],
parents: Array[Array[Int]]): Set[Seq[Int]] = {
val res = new mutable.HashSet[Seq[Int]]()
for (node <- cycleNodes) {
var cycle = new mutable.ArrayBuffer[Int]()
cycle += node
var other = parents(node)(node)
do {
cycle += other
other = parents(other)(node)
} while(other != node)
res += cycle.sorted
}
res.toSet
}
ve sonucu test etmek için küçük bir ana yöntem
def main(args: Array[String]): Unit = {
val n = 3
val weights = Array(Array(NO_EDGE, 1, NO_EDGE), Array(NO_EDGE, NO_EDGE, 1), Array(1, NO_EDGE, NO_EDGE))
val parents = Array(Array(-1, 1, -1), Array(-1, -1, 2), Array(0, -1, -1))
shortestPathWithParentTracking(weights, parents)
val cycleNodes = parents.indices.filter(i => parents(i)(i) < NO_EDGE)
val cycles: Set[Seq[Int]] = recoverCycles(cycleNodes, parents)
println("The following minimal cycle found:")
cycles.foreach(c => println(c.mkString))
println(s"Total: ${cycles.size} cycle found")
}
ve çıktı
The following minimal cycle found:
012
Total: 1 cycle found
Yönlendirilmemiş grafik durumunda, yakın zamanda yayınlanan bir makale ( Yönlendirilmemiş grafiklerde döngülerin ve st-yolların optimum listesi ) asimptotik olarak en uygun çözümü sunar. Burada okuyabilirsiniz http://arxiv.org/abs/1205.2766 veya burada http://dl.acm.org/citation.cfm?id=2627951 Sorunuza cevap vermediğini biliyorum, ancak sorunuz yön belirtmiyor, yine de Google araması için yararlı olabilir
X düğümünden başlayın ve tüm alt düğümleri kontrol edin (yönlendirilmemişse üst ve alt düğümler eşdeğerdir). Bu alt düğümleri X'in çocukları olarak işaretleyin. Bu tür herhangi bir alt düğümden, X'in 2 adım uzakta olduğu işaretlenen A, X '' in çocukları olduğunu işaretleyin.). Daha sonra X'e basar ve bunu X'in alt öğesi olarak işaretlerseniz, bu X'in 3 düğümlü bir döngüde olduğu anlamına gelir. Üst öğeye geri izlemek kolaydır (olduğu gibi, algoritmanın bunun için desteği yoktur, bu nedenle hangi üst öğenin X 'olduğunu bulursunuz).
Not: Grafik yönlendirilmemişse veya çift yönlü kenarları varsa, bir döngü için aynı kenarı iki kez geçmek istemediğinizi varsayarsak, bu algoritma daha karmaşık hale gelir.
İstediğiniz bir grafikteki tüm temel devreleri bulmaksa, 1970'ten beri bir kağıtta bulunan JAMES C. TIERNAN tarafından EC algoritmasını kullanabilirsiniz.
Çok orijinal I (umut hiçbir hata aşağıda gösterilmiştir orada toplamak vardır) php uygulamak başardı olarak AK algoritması. Varsa döngüler de bulabilir. Bu uygulamadaki (orijinali klonlamaya çalışan) devreler sıfır olmayan elemanlardır. Burada sıfır, var olmama anlamına gelir (bildiğimiz gibi null).
Aşağıdakilerin dışında, algoritmaya daha bağımsız bir uygulama sağlayan başka bir uygulama da izlenir, bu da düğümlerin negatif sayılardan bile, örneğin -4, -3, -2, vb.
Her iki durumda da düğümlerin sıralı olması gerekir.
Orijinal makaleyi incelemeniz gerekebilir, James C. Tiernan Temel Devre Algoritması
<?php
echo "<pre><br><br>";
$G = array(
1=>array(1,2,3),
2=>array(1,2,3),
3=>array(1,2,3)
);
define('N',key(array_slice($G, -1, 1, true)));
$P = array(1=>0,2=>0,3=>0,4=>0,5=>0);
$H = array(1=>$P, 2=>$P, 3=>$P, 4=>$P, 5=>$P );
$k = 1;
$P[$k] = key($G);
$Circ = array();
#[Path Extension]
EC2_Path_Extension:
foreach($G[$P[$k]] as $j => $child ){
if( $child>$P[1] and in_array($child, $P)===false and in_array($child, $H[$P[$k]])===false ){
$k++;
$P[$k] = $child;
goto EC2_Path_Extension;
} }
#[EC3 Circuit Confirmation]
if( in_array($P[1], $G[$P[$k]])===true ){//if PATH[1] is not child of PATH[current] then don't have a cycle
$Circ[] = $P;
}
#[EC4 Vertex Closure]
if($k===1){
goto EC5_Advance_Initial_Vertex;
}
//afou den ksana theoreitai einai asfales na svisoume
for( $m=1; $m<=N; $m++){//H[P[k], m] <- O, m = 1, 2, . . . , N
if( $H[$P[$k-1]][$m]===0 ){
$H[$P[$k-1]][$m]=$P[$k];
break(1);
}
}
for( $m=1; $m<=N; $m++ ){//H[P[k], m] <- O, m = 1, 2, . . . , N
$H[$P[$k]][$m]=0;
}
$P[$k]=0;
$k--;
goto EC2_Path_Extension;
#[EC5 Advance Initial Vertex]
EC5_Advance_Initial_Vertex:
if($P[1] === N){
goto EC6_Terminate;
}
$P[1]++;
$k=1;
$H=array(
1=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
2=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
3=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
4=>array(1=>0,2=>0,3=>0,4=>0,5=>0),
5=>array(1=>0,2=>0,3=>0,4=>0,5=>0)
);
goto EC2_Path_Extension;
#[EC5 Advance Initial Vertex]
EC6_Terminate:
print_r($Circ);
?>
o zaman bu diğer uygulama, grafikten daha bağımsız, goto olmadan ve dizi değerleri olmadan, bunun yerine dizi anahtarları, yol, grafik ve devreler dizi anahtarları olarak saklanır (isterseniz dizi değerlerini kullanın, sadece gerekli olanları değiştirin çizgiler). Örnek grafik bağımsızlığını göstermek için -4'ten başlar.
<?php
$G = array(
-4=>array(-4=>true,-3=>true,-2=>true),
-3=>array(-4=>true,-3=>true,-2=>true),
-2=>array(-4=>true,-3=>true,-2=>true)
);
$C = array();
EC($G,$C);
echo "<pre>";
print_r($C);
function EC($G, &$C){
$CNST_not_closed = false; // this flag indicates no closure
$CNST_closed = true; // this flag indicates closure
// define the state where there is no closures for some node
$tmp_first_node = key($G); // first node = first key
$tmp_last_node = $tmp_first_node-1+count($G); // last node = last key
$CNST_closure_reset = array();
for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
$CNST_closure_reset[$k] = $CNST_not_closed;
}
// define the state where there is no closure for all nodes
for($k=$tmp_first_node; $k<=$tmp_last_node; $k++){
$H[$k] = $CNST_closure_reset; // Key in the closure arrays represent nodes
}
unset($tmp_first_node);
unset($tmp_last_node);
# Start algorithm
foreach($G as $init_node => $children){#[Jump to initial node set]
#[Initial Node Set]
$P = array(); // declare at starup, remove the old $init_node from path on loop
$P[$init_node]=true; // the first key in P is always the new initial node
$k=$init_node; // update the current node
// On loop H[old_init_node] is not cleared cause is never checked again
do{#Path 1,3,7,4 jump here to extend father 7
do{#Path from 1,3,8,5 became 2,4,8,5,6 jump here to extend child 6
$new_expansion = false;
foreach( $G[$k] as $child => $foo ){#Consider each child of 7 or 6
if( $child>$init_node and isset($P[$child])===false and $H[$k][$child]===$CNST_not_closed ){
$P[$child]=true; // add this child to the path
$k = $child; // update the current node
$new_expansion=true;// set the flag for expanding the child of k
break(1); // we are done, one child at a time
} } }while(($new_expansion===true));// Do while a new child has been added to the path
# If the first node is child of the last we have a circuit
if( isset($G[$k][$init_node])===true ){
$C[] = $P; // Leaving this out of closure will catch loops to
}
# Closure
if($k>$init_node){ //if k>init_node then alwaya count(P)>1, so proceed to closure
$new_expansion=true; // $new_expansion is never true, set true to expand father of k
unset($P[$k]); // remove k from path
end($P); $k_father = key($P); // get father of k
$H[$k_father][$k]=$CNST_closed; // mark k as closed
$H[$k] = $CNST_closure_reset; // reset k closure
$k = $k_father; // update k
} } while($new_expansion===true);//if we don't wnter the if block m has the old k$k_father_old = $k;
// Advance Initial Vertex Context
}//foreach initial
}//function
?>
AT'yi analiz ettim ve belgeledim ama maalesef belgeler Yunanca.
Bir DAG'daki tüm döngüleri bulmada iki adım (algoritma) vardır.
İlk adım, güçlü bir şekilde bağlı bileşenler kümesini bulmak için Tarjan'ın algoritmasını kullanmaktır.
İkinci adım, bağlı bileşenler içindeki döngüleri (yolları) bulmaktır. Benim önerim Hierholzer algoritmasının değiştirilmiş bir versiyonunu kullanmak.
Fikir:
İşte bir test senaryosuna sahip bir Java uygulamasına bağlantı:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
Johnson'un algoritmasından (en azından daha büyük grafikler için) daha verimli görünen aşağıdaki algoritmaya rastladım. Ancak Tarjan'ın algoritmasına kıyasla performansından emin değilim.
Ayrıca, şimdiye kadar sadece üçgenler için kontrol ettim. İlgileniyorsanız, lütfen Norishige Chiba ve Takao Nishizeki'nin "Arboricity ve Alt Çizgi Listeleme Algoritmaları" na bakın ( http://dx.doi.org/10.1137/0214017 )
Ayrık küme bağlantılı listeleri kullanarak Javascript çözümü. Daha hızlı çalışma süreleri için ayrık ayarlanmış ormanlara yükseltilebilir.
var input = '5\nYYNNN\nYYYNN\nNYYNN\nNNNYN\nNNNNY'
console.log(input);
//above solution should be 3 because the components are
//{0,1,2}, because {0,1} and {1,2} therefore {0,1,2}
//{3}
//{4}
//MIT license, authored by Ling Qing Meng
//'4\nYYNN\nYYYN\nNYYN\nNNNY'
//Read Input, preformatting
var reformat = input.split(/\n/);
var N = reformat[0];
var adjMatrix = [];
for (var i = 1; i < reformat.length; i++) {
adjMatrix.push(reformat[i]);
}
//for (each person x from 1 to N) CREATE-SET(x)
var sets = [];
for (var i = 0; i < N; i++) {
var s = new LinkedList();
s.add(i);
sets.push(s);
}
//populate friend potentials using combinatorics, then filters
var people = [];
var friends = [];
for (var i = 0; i < N; i++) {
people.push(i);
}
var potentialFriends = k_combinations(people,2);
for (var i = 0; i < potentialFriends.length; i++){
if (isFriend(adjMatrix,potentialFriends[i]) === 'Y'){
friends.push(potentialFriends[i]);
}
}
//for (each pair of friends (x y) ) if (FIND-SET(x) != FIND-SET(y)) MERGE-SETS(x, y)
for (var i = 0; i < friends.length; i++) {
var x = friends[i][0];
var y = friends[i][1];
if (FindSet(x) != FindSet(y)) {
sets.push(MergeSet(x,y));
}
}
for (var i = 0; i < sets.length; i++) {
//sets[i].traverse();
}
console.log('How many distinct connected components?',sets.length);
//Linked List data structures neccesary for above to work
function Node(){
this.data = null;
this.next = null;
}
function LinkedList(){
this.head = null;
this.tail = null;
this.size = 0;
// Add node to the end
this.add = function(data){
var node = new Node();
node.data = data;
if (this.head == null){
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
this.size++;
};
this.contains = function(data) {
if (this.head.data === data)
return this;
var next = this.head.next;
while (next !== null) {
if (next.data === data) {
return this;
}
next = next.next;
}
return null;
};
this.traverse = function() {
var current = this.head;
var toPrint = '';
while (current !== null) {
//callback.call(this, current); put callback as an argument to top function
toPrint += current.data.toString() + ' ';
current = current.next;
}
console.log('list data: ',toPrint);
}
this.merge = function(list) {
var current = this.head;
var next = current.next;
while (next !== null) {
current = next;
next = next.next;
}
current.next = list.head;
this.size += list.size;
return this;
};
this.reverse = function() {
if (this.head == null)
return;
if (this.head.next == null)
return;
var currentNode = this.head;
var nextNode = this.head.next;
var prevNode = this.head;
this.head.next = null;
while (nextNode != null) {
currentNode = nextNode;
nextNode = currentNode.next;
currentNode.next = prevNode;
prevNode = currentNode;
}
this.head = currentNode;
return this;
}
}
/**
* GENERAL HELPER FUNCTIONS
*/
function FindSet(x) {
for (var i = 0; i < sets.length; i++){
if (sets[i].contains(x) != null) {
return sets[i].contains(x);
}
}
return null;
}
function MergeSet(x,y) {
var listA,listB;
for (var i = 0; i < sets.length; i++){
if (sets[i].contains(x) != null) {
listA = sets[i].contains(x);
sets.splice(i,1);
}
}
for (var i = 0; i < sets.length; i++) {
if (sets[i].contains(y) != null) {
listB = sets[i].contains(y);
sets.splice(i,1);
}
}
var res = MergeLists(listA,listB);
return res;
}
function MergeLists(listA, listB) {
var listC = new LinkedList();
listA.merge(listB);
listC = listA;
return listC;
}
//access matrix by i,j -> returns 'Y' or 'N'
function isFriend(matrix, pair){
return matrix[pair[0]].charAt(pair[1]);
}
function k_combinations(set, k) {
var i, j, combs, head, tailcombs;
if (k > set.length || k <= 0) {
return [];
}
if (k == set.length) {
return [set];
}
if (k == 1) {
combs = [];
for (i = 0; i < set.length; i++) {
combs.push([set[i]]);
}
return combs;
}
// Assert {1 < k < set.length}
combs = [];
for (i = 0; i < set.length - k + 1; i++) {
head = set.slice(i, i+1);
tailcombs = k_combinations(set.slice(i + 1), k - 1);
for (j = 0; j < tailcombs.length; j++) {
combs.push(head.concat(tailcombs[j]));
}
}
return combs;
}
Başlangıç düğümünden DFS, geçiş sırasında DFS yolunu takip edin ve s yolundan v düğümünden bir kenar bulursanız yolu kaydedin. (v, s) DFS ağacında bir arka kenardır ve bu nedenle s içeren bir döngüyü gösterir.
Permütasyon Döngüsü ile ilgili sorunuzla ilgili daha fazla bilgiyi buradan edinebilirsiniz: https://www.codechef.com/problems/PCYCLE
Bu kodu deneyebilirsiniz (boyut ve rakam numarasını girin):
# include<cstdio>
using namespace std;
int main()
{
int n;
scanf("%d",&n);
int num[1000];
int visited[1000]={0};
int vindex[2000];
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
int t_visited=0;
int cycles=0;
int start=0, index;
while(t_visited < n)
{
for(int i=1;i<=n;i++)
{
if(visited[i]==0)
{
vindex[start]=i;
visited[i]=1;
t_visited++;
index=start;
break;
}
}
while(true)
{
index++;
vindex[index]=num[vindex[index-1]];
if(vindex[index]==vindex[start])
break;
visited[vindex[index]]=1;
t_visited++;
}
vindex[++index]=0;
start=index+1;
cycles++;
}
printf("%d\n",cycles,vindex[0]);
for(int i=0;i<(n+2*cycles);i++)
{
if(vindex[i]==0)
printf("\n");
else
printf("%d ",vindex[i]);
}
}
İkinci katın cevabında sözde kod için DFS c ++ sürümü:
void findCircleUnit(int start, int v, bool* visited, vector<int>& path) {
if(visited[v]) {
if(v == start) {
for(auto c : path)
cout << c << " ";
cout << endl;
return;
}
else
return;
}
visited[v] = true;
path.push_back(v);
for(auto i : G[v])
findCircleUnit(start, i, visited, path);
visited[v] = false;
path.pop_back();
}