La soluzione concreta dipende dalla definizione di una sottostruttura. Si consideri il seguente BST:
5
3
2
4
8
-
9
E vogliamo trovare le sottostrutture nella gamma [4,8]
. È ovvio che il nodo 4
appartiene all'output. Ma per quanto riguarda l'altro mezzo albero? Se una sottostruttura si riferisce a un nodo con tutti i suoi figli, allora questo è l'intero risultato.Se una sottostruttura è in realtà un sottoinsieme dei nodi di input, i nodi 5
e 8
appartengono al risultato, ma le loro connessioni ai nodi 3
e 9
devono essere rimosse.
In ogni caso, il seguente algoritmo può gestire entrambi. Il preprocessore definisce WHOLE_SUBTREES
definisce se i sottoalberi sono interi sottocomponenti con tutti i bambini.
static List<BSTNode> FindSubtreesInRange(BSTNode root, int rangeMin, int rangeMax)
{
var result = new List<BSTNode>();
if (IsTreeWithinRange(root, rangeMin, rangeMax, int.MinValue, int.MaxValue, result))
result.Add(root);
return result;
}
static bool IsTreeWithinRange(BSTNode root, int rangeMin, int rangeMax, int treeRangeMin, int treeRangeMax, List<BSTNode> resultList)
{
if (treeRangeMin >= rangeMin && treeRangeMax <= rangeMax)
return true;
if (treeRangeMin > rangeMax || treeRangeMax < rangeMin)
return false;
if (root.Key < rangeMin)
{
if (root.Right != null && IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList))
resultList.Add(root.Right);
return false;
}
if (root.Key > rangeMax)
{
if (root.Left != null && IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList))
resultList.Add(root.Left);
return false;
}
if (root.Left == null && root.Right == null)
return true;
if (root.Left == null)
{
#if WHOLE_SUBTREES
if (!IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList))
root.Right = null;
return true;
#else
return IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList);
#endif
}
if (root.Right == null)
{
#if WHOLE_SUBTREES
if (!IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList))
root.Left = null;
return true;
#else
return IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList);
#endif
}
var leftInRange = IsTreeWithinRange(root.Left, rangeMin, rangeMax, treeRangeMin, root.Key, resultList);
var rightInRange = IsTreeWithinRange(root.Right, rangeMin, rangeMax, root.Key + 1, treeRangeMax, resultList);
if (leftInRange && rightInRange)
return true;
#if WHOLE_SUBTREES
if (!leftInRange)
root.Left = null;
if (!rightInRange)
root.Right = null;
return true;
#else
if (leftInRange)
resultList.Add(root.Left);
if (rightInRange)
resultList.Add(root.Right);
return false;
#endif
}
L'idea è la seguente: Se solo una sottostruttura di un dato nodo è entro l'intervallo dato, allora questo deve essere la radice di una nuova sottostruttura. Se entrambi si trovano nell'intervallo, non sono la radice di un albero secondario. Invece, il livello genitore dovrebbe gestire la decisione secondo.
L'algoritmo inizia con quanto segue: Attraversiamo l'albero e ricordiamo in quale intervallo possono essere i tasti (treeRangeMin/Max
). Ciò consente un controllo rapido se un'intera sottostruttura si trova nell'intervallo specificato (prima dichiarazione del metodo IsTreeWithinRange
.
Le due istruzioni successive gestiscono il caso se la chiave del nodo corrente si trova al di fuori dell'intervallo specificato. Quindi solo una delle sue sottostrutture potrebbe essere all'interno dell'intervallo .. Se questo è il caso, questo sottostruttura viene aggiunto all'elenco dei risultati
Successivamente, controlliamo se esistono i sottoalberi. Se entrambi non lo sono, l'albero corrente è completamente contenuto nell'intervallo.
Se esiste solo un sottoalbero, l'azione varia a seconda che si possano suddividere gli alberi. Se si può suddividere l'albero, accade quanto segue: Se il sottostruttura non è all'interno l'intervallo, lo interrompiamo e restituiamo true (poiché il nodo corrente è contenuto nell'intervallo specificato). Se non possiamo dividere gli alberi, propaghiamo semplicemente il risultato della chiamata ricorsiva.
Infine, se entrambi i bambini esistono. Se uno di essi non è contenuto nell'intervallo, lo interrompiamo (se ci è permesso). Se non siamo autorizzati, aggiungiamo la sottostruttura alla lista dei risultati che si trova all'interno dell'intervallo dato.
Si prega di rispondere selezionando una risposta, in modo da conoscere la migliore risposta al problema indicato. –