Конкретное решение зависит от определения поддерева. Рассмотрим следующий BST:
5
3
2
4
8
-
9
И мы хотим, чтобы найти поддеревьев в диапазоне [4,8]
. Очевидно, что узел 4
принадлежит к выходу. Но как насчет другой половины дерева? Если поддерево ссылается на узел со всеми его дочерними элементами, то это весь результат.Если поддерево фактически является подмножеством входных узлов, узлы 5
и 8
относятся к результату, но их соединения с узлами 3
и 9
должны быть удалены.
В любом случае, следующий алгоритм может обрабатывать оба. Препроцессор, определяющий WHOLE_SUBTREES
, определяет, являются ли поддеревья целыми субкомпонентами со всеми дочерними элементами.
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
}
Идея заключается в следующем: Если только один поддерево данного узла находится в пределах заданного диапазона, то это должно быть корнем нового поддерева. Если оба лежат в диапазоне, то они не являются корнем поддерева. Вместо этого родительский уровень должен обрабатывать соответствующее решение.
Алгоритм начинается со следующего: мы пересекаем дерево и помним, в каких диапазонах могут быть ключи (treeRangeMin/Max
). Это позволяет быстро проверить, находится ли в данном диапазоне целое поддерево (первый оператор метода IsTreeWithinRange
.
Следующие два оператора обрабатывают регистр, если ключ текущего узла находится за пределами заданного диапазона. Тогда только одно из его поддеревьев может быть в пределах диапазона.Если это так, это поддерево добавляется в список результатов.
Далее мы проверяем, существуют ли поддеревья. Если и то и другое, то текущее дерево полностью содержится в пределах диапазона.
Если существует только одно поддерево, действие различается в зависимости от того, можно ли разделить деревья. Если мы можем разделить дерево, произойдет следующее: если поддерево не находится внутри диапазон, мы отключили его и вернем true (поскольку текущий узел содержится в заданном диапазоне). Если мы не можем разделить деревья, мы просто распространяем результат рекурсивного вызова.
Наконец, если оба ребенка существуют. Если один из них не содержится в пределах диапазона, мы отключили его (если нам разрешено). Если нам не разрешено, мы добавляем поддерево в список результатов, который находится в заданном диапазоне.
Пожалуйста, ответьте, выбрав ответ, чтобы мы знали лучший ответ на данную проблему. –