Ich habe degenerate tree
(sieht wie ein Array oder eine doppelt verkettete Liste aus). Zum Beispiel ist es dieser Baum:
Jede Kante hat ein gewisses Gewicht. Ich möchte alle gleichen Pfade finden, die in jedem Eckpunkt beginnen.
Mit anderen Worten, ich möchte alle Tupel (v1, v, v2) erhalten, wobei v1 und v2 ein beliebiger Vor- und Nachfahren sind, so dass c(v1, v) = c(v, v2)
.
Lassen Sie die Kanten die folgenden Gewichte haben (es ist nur ein Beispiel):
a-b = 3
b-c = 1
c-d = 1
d-e = 1
Dann:
A
hat keinen gleichen Pfad (es gibt keinen Vertex von der linken Seite). B
hat ein gleiches Paar. Der Pfad B-A
entspricht dem Pfad B-E
(3 == 3)
. C
hat ein gleiches Paar. Der Pfad B-C
entspricht dem Pfad C-D
(1 == 1)
. D
hat ein gleiches Paar. Der Pfad C-D
entspricht dem Pfad D-E
(1 == 1)
. E
hat keinen gleichen Pfad (es gibt keinen Vertex von der rechten Seite). Ich implementiere einen einfachen Algorithmus, der in O(n^2)
funktioniert. Aber es ist zu langsam für mich.
Sie schreiben in Kommentaren, dass Ihr aktueller Ansatz
istEs scheint, ich suche nach einer Möglichkeit, die Konstante in O (n ^ 2) zu verringern. ich wähle irgendein Eckpunkt. Dann erstelle ich zwei Sets. Dann fülle ich diese Sets mit Partialsummen, während von diesem Eckpunkt zum Start von Baum und zu iterieren Ende des Baumes. Dann finde ich einen Schnittpunkt und erhalte die Anzahl der Pfade von diesem Eckpunkt. Dann wiederhole ich den Algorithmus für alle anderen Knoten.
Es gibt einen einfacheren und, ich denke, schnelleren Ansatz von O(n^2)
, basierend auf der sogenannten Zwei-Zeiger-Methode.
Für jedes vertix v
gehen Sie gleichzeitig in zwei mögliche Richtungen. Bewegen Sie einen "Zeiger" zu einem Scheitelpunkt ( vl
) in eine Richtung und einen anderen ( vr
) in eine andere Richtung und versuchen Sie, den Abstand von v
zu vl
so nahe wie möglich von% co_de zu halten % bis v
wie möglich. Jedes Mal, wenn diese Abstände gleich sind, haben Sie gleiche Pfade.
(Durch Vorberechnung der Präfixsummen finden Sie vr
in O (1).)
Es ist leicht zu sehen, dass kein gleiches Paar verpasst wird, vorausgesetzt, dass Sie keine Kanten mit der Länge 0 haben.
In Bezug auf eine schnellere Lösung, wenn Sie alle Paare listen wollen, können Sie es nicht schneller machen, weil die Anzahl der Paare im schlimmsten Fall 0 (n ^ 2) ist. Aber wenn Sie nur die Menge dieser Paare benötigen, können schnellere Algorithmen existieren.
UPD : Ich habe einen anderen Algorithmus für die Berechnung der Menge entwickelt, der schneller sein könnte, wenn Ihre Kanten eher kurz sind. Wenn Sie die Gesamtlänge Ihrer Kette (Summe aller Kantengewichte) als dist
angeben, wird der Algorithmus in L
ausgeführt. Es ist jedoch konzeptionell viel fortschrittlicher und fortgeschrittener in der Codierung auch.
Zunächst einige theoretische Überlegungen. Betrachten Sie einen Vertex O(L log L)
. Lassen Sie uns zwei Arrays, v
und a
, nicht die null-indizierten Arrays im C-Stil, sondern Arrays mit Indexierung von b
bis -L
.
Definieren wir
L
, i>0
wenn zu rechts von a[i]=1
für die Entfernung genau v
dort
ist ein Eckpunkt, sonst i
a[i]=0
, i=0
a[i]≡a[0]=1
, i<0
wenn zu links von a[i]=1
für die Entfernung genau v
es gibt einen Eckpunkt, sonst -i
Ein einfaches Verständnis dieses Arrays ist wie folgt. Strecken Sie Ihren Graphen und legen Sie ihn entlang der Koordinatenachse, so dass jede Kante die Länge hat, die ihrem Gewicht entspricht, und dass der Scheitelpunkt a[i]=0
im Ursprung liegt. Dann v
wenn es einen Knoten an der Koordinate a[i]=1
gibt.
Für Ihr Beispiel und für den Scheitelpunkt "b" gewählt als i
:
Für ein anderes Array, Array v
, definieren wir die Werte in einer symmetrischen Weise in Bezug auf den Ursprung, als ob wir die Richtung der Achse invertiert hätten:
b
, i>0
wenn bis links von b[i]=1
für die Entfernung genau v
dort
ist ein Eckpunkt, sonst i
b[i]=0
, i=0
b[i]≡b[0]=1
, i<0
wenn zu rechts von b[i]=1
für die Entfernung genau v
es gibt einen Scheitelpunkt, sonst -i
Betrachten Sie nun ein drittes Array b[i]=0
, so dass c
, Sternchen hier für normale Multiplikation bleibt. Offensichtlich c[i]=a[i]*b[i]
, wenn der Pfad der Länge c[i]=1
auf der linken Seite in einem Scheitelpunkt endet und der Pfad der Länge abs(i)
auf der rechten Seite in einem Scheitelpunkt endet. Für abs(i)
entspricht jede Position in i>0
mit c
dem Pfad, den Sie benötigen. Es gibt auch negative Positionen ( c[i]=1
mit c[i]=1
), die nur die positiven Positionen widerspiegeln, und eine weitere Position mit i<0
, nämlich Position c[i]=1
.
Berechnen Sie die Summe aller Elemente in i=0
. Diese Summe ist c
, wobei sum(c)=2P+1
die Gesamtzahl der Pfade ist, die Sie benötigen, wobei P
der Mittelpunkt ist. Wenn Sie also v
kennen, können Sie einfach sum(c)
ermitteln.
Betrachten wir nun die Arrays P
und a
näher und wie verändern sie sich, wenn wir den Vertex b
ändern. Lassen Sie uns v
den linken Eckpunkt (das Wurzelverzeichnis Ihres Baumes) und v0
und a0
die entsprechenden b0
und a
Arrays für diesen Eckpunkt angeben.
Für einen beliebigen Vertex b
bedeutet v
. Dann ist es leicht zu sehen, dass für den Vertex d=dist(v0,v)
die Arrays v
und a
nur die Arrays b
und a0
um b0
verschoben sind:
Es ist offensichtlich, wenn Sie sich an das Bild mit dem entlang einer Koordinatenachse gestreckten Baum erinnern.
Betrachten wir nun ein weiteres Array, d
(ein Array für alle Scheitelpunkte), und für jeden Scheitelpunkt S
geben wir den Wert von v
in das Element sum(c)
( S[d]
und d
hängt von c
ab.
Genauer gesagt, lassen Sie uns das Array v
für jedes S
Sobald wir das Array d
kennen, können wir über Scheitelpunkte iterieren und für jeden Scheitelpunkt S
sein v
einfach als sum(c)
mit S[d]
erhalten, weil wir für jeden Scheitelpunkt d=dist(v,v0)
% co_de haben %.
Aber die Formel für v
ist sehr einfach: sum(c)=sum(a0[i+d]*b0[i-d])
ist nur die Faltung des S
und S
Sequenzen. (Die Formel folgt nicht genau der Definition, sondern lässt sich leicht in die genaue Definitionsform ändern.)
Was wir jetzt brauchen, ist a0
und b0
(was wir in a0
time und space berechnen können), berechnet das b0
Array. Danach können wir über O(L)
array iterieren und einfach die Anzahl der Pfade aus S
extrahieren.
Direkte Anwendung der obigen Formel ist S
. Jedoch kann die Faltung von zwei Folgen in S[d]=2P+1
berechnet werden, indem der Fast-Fourier-Transformationsalgorithmus angewendet wird. Darüber hinaus können Sie eine ähnliche Zahlentheoretische Transformation anwenden (ich weiß nicht, ob es eine bessere Verknüpfung gibt), um mit Ganzzahlen zu arbeiten nur und vermeiden Sie Präzisionsprobleme.
Also wird der allgemeine Umriss des Algorithmus
%Vor% (Ich nenne es O(L^2)
, weil O(L log L)
nicht genau eine Faltung ist, sondern ein sehr ähnliches Objekt, für das ein ähnlicher Algorithmus angewendet werden kann. Außerdem können Sie corrected_convolution
und dann S
definieren die richtige Faltung.)
Tags und Links algorithm graph sum doubly-linked-list