具体的解决方案取决于子树的定义。考虑以下 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(因为当前节点包含在给定范围内)。如果我们不能分裂树,我们只是传播递归调用的结果。
最后,如果两个孩子都存在。如果其中一个不在范围内,我们将其切断(如果允许的话)。如果不允许,我们将子树添加到给定范围内的结果列表中。