È possibile utilizzare uno recursive DFS algorithm con alcune modifiche.
Non hai detto quale lingua usi, quindi spero che C# sia OK per te.
Definiamo una classe per il nostro nodo della struttura:
public class Node
{
public int Id;
public bool UsedOnce = false;
public bool Visited = false;
public Node[] Children;
}
Date un'occhiata a UsedOnce
variabile - si può guardare piuttosto ambigua.
UsedOnce
equivale a true
se questo nodo è stato utilizzato una sola volta in un'uscita. Dato che abbiamo un albero, significa anche che un fronte da questo nodo al suo genitore è stato usato una volta in un output (in un albero, ogni nodo ha solo un bordo padre che è il bordo del suo genitore). Leggi attentamente per non diventare confuso in futuro.
Qui abbiamo una semplice implementazione di algoritmo di ricerca in profondità.
Tutta la magia sarà trattata in un metodo di output.
List<Node> currentPath = new List<Node>(); // list of visited nodes
public void DFS(Node node)
{
if (node.Children.Length == 0) // if it is a leaf (no children) - output
{
OutputAndMarkAsUsedOnce(); // Here goes the magic...
return;
}
foreach (var child in node.Children)
{
if (!child.Visited) // for every not visited children call DFS
{
child.Visited = true;
currentPath.Add(child);
DFS(child);
currentPath.Remove(child);
child.Visited = false;
}
}
}
Se OutputAndMarkedAsUsedOnce
solo in output un currentPath
contenuti, allora avremmo una potenza pianura DFS in questo modo:
1 2 5
1 2 6
1 3 7
1 4 8
1 4 9
Ora, abbiamo bisogno di usare il nostro UsedOnce
. Let's trova l'ultimo nodo used-once (che è già stato in un'uscita) nel percorso corrente e restituisce tutto il percorso da questo nodo allo. È garantito che tale nodo esiste perché, almeno l'ultimo nodo in un percorso non è mai stato incontrato prima e non può essere contrassegnato come usato una sola volta.
Ad esempio, se il percorso corrente è "1 2 3 4 5" e 1, 2, 3 sono contrassegnati come utilizzati una volta - quindi output "3 4 5".
Nel tuo esempio:
- Siamo al "1 2 5". Tutti sono inutilizzati, uscita "1 2 5" e marca 1, 2, 5 come usato una volta
- Ora, siamo a "1 2 6". 1, 2 sono usati - 2 è l'ultimo. Uscita da 2 in modo inclusivo, "2 6", segnare 2 e 6 come usato.
- Ora siamo a "1 3 7", 1 è usato, l'unico e l'ultimo. Uscita da 1 compresa, "1 3 7". Segna 1, 3, 7 come usato.
- Ora siamo a "1 4 8". 1 è usato, l'unico e l'ultimo. Uscita "1 4 8".
- Ora siamo a "1 4 9". 1, 4 sono usati. Uscita da 4 - "4 9".
Funziona perché in un albero "nodo utilizzato" significa "usato (l'unico genitore) bordo tra esso e il suo genitore". Quindi, in realtà contrassegna i bordi usati e non li emette di nuovo.
Ad esempio, quando si contrassegna 2, 5 come usato, significa che si contrassegnano i bordi 1-2 e 2-5. Quindi, quando usiamo "1 2 6" - non produciamo bordi "1-2" perché sono usati, ma output "2-6".
Il nodo radice di marcatura (nodo 1) utilizzato una volta non influenza l'output perché il suo valore non viene mai verificato. Ha una spiegazione fisica - il nodo radice non ha margini parentali.
Siamo spiacenti per una spiegazione scarsa. È piuttosto difficile spiegare un algoritmo sugli alberi senza disegnare :) Sentiti libero di porre domande sugli algoritmi o C#.
Questo è il funzionamento IDEOne demo.
P.S. Questo codice è, probabilmente, non è un buon codice corretto C# (proprietà automatiche evitate, LINQ evitato) per renderlo comprensibile agli altri codificatori.
Ovviamente, questo algoritmo non è perfetto - possiamo rimuovere currentPath
perché in un albero il percorso è facilmente recuperabile; possiamo migliorare l'output; possiamo incapsulare questo algoritmo in una classe. Ho appena provato a mostrare la soluzione comune.
Cercate, il codice di lavoro formali, o semplicemente un algoritmo? –
Il codice di lavoro è sicuramente meglio, di solito uso Python piuttosto che lo pseudo codice in quanto è più facile da testare. –
Suggerimento: aggiungere un vincolo in modo che la soluzione sia unica. –