与其直接跳入在这里工作的算法,我想给出一系列观察结果,最终得出一个非常好的算法来解决这个问题。
首先,假设对于树中的每个节点,您都知道以该节点为根的子树中的最大值和最小值。 (让我们将它们表示为 min(x) 和 max(x),其中 x 是树中的一个节点)。鉴于此信息,我们可以进行以下观察:
观察 1: 如果 x ≤ max(x.left) 或 x ≥ min(y.right),则节点 x 是非 BST 的根
这不是一个 if-and-only-if 条件 - 它只是一个“如果” - 但它是一个有用的观察。这样做的原因是,如果 x ≤ max(x.left),那么 x 的左子树中有一个不小于 x 的节点,这意味着以 x 为根的树不是 BST,并且如果 x > min( x.right),那么 x 的右子树中有一个不大于 x 的节点,这意味着以 x 为根的树不是 BST。
现在,x max(x.left) 的任何节点不一定是 BST 的根。以这棵树为例:
4
/ \
1 6
/ \
2 5
这里,根节点大于其左子树中的所有内容,并且小于其右子树中的所有内容,但整个树本身不是 BST。原因是植根于 1 和 6 的树不是 BST。这导致了一个有用的观察:
观察 2: 如果 x > max(x.left) 且 x
这个结果的证明的简单草图:如果 x.left 和 x.right 是 BST,那么对树进行中序遍历将列出 x.left 中的所有值以升序排列,然后是 x,然后x.right 中的所有值按升序排列。由于 x > max(x.left) 和 x
这两个属性提供了一种非常好的方法来查找树中不是 BST 根的每个节点。思路是从叶子向上遍历树中的节点,检查每个节点的值是否大于其左子树的最大值且小于其右子树的最小值,然后检查其左右子树是否为BST .您可以通过后序遍历来做到这一点,如下所示:
/* Does a postorder traversal of the tree, tagging each node with its
* subtree min, subtree max, and whether the node is the root of a
* BST.
*/
function findNonBSTs(r) {
/* Edge case for an empty tree. */
if (r is null) return;
/* Process children - this is a postorder traversal. This also
* tags each child with information about its min and max values
* and whether it's a BST.
*/
findNonBSTs(r.left);
findNonBSTs(r.right);
/* If either subtree isn't a BST, we're done. */
if ((r.left != null && !r.left.isBST) ||
(r.right != null && !r.right.isBST)) {
r.isBST = false;
return;
}
/* Otherwise, both children are BSTs. Check against the min and
* max values of those subtrees to make sure we're in range.
*/
if ((r.left != null && r.left.max >= r.value) ||
(r.right != null && r.right.min <= r.value)) {
r.isBST = false;
return;
}
/* Otherwise, we're a BST, and our min and max value can be
* computed from the left and right children.
*/
r.isBST = true;
r.min = (r.left != null? r.left.min : r.value);
r.max = (r.right != null? r.right.max : r.value);
}
如果你在树上运行了这个过程,每个节点都将被标记为是否是二叉搜索树。从那里开始,您所要做的就是再次遍历树以找到不是 BST 的最深节点。我将把它作为一个众所周知的练习留给读者。 :-)
希望这会有所帮助!