给定一棵二叉树,我想找出其中最大的二叉搜索子树。
朴素的方法:
我心中有一个朴素的方法,遍历树的每个节点并将此节点传递给isBST函数。如果它是一个BST,我还将跟踪子树中的节点数。
是否有比这更好的方法?
给定一棵二叉树,我想找出其中最大的二叉搜索子树。
朴素的方法:
我心中有一个朴素的方法,遍历树的每个节点并将此节点传递给isBST函数。如果它是一个BST,我还将跟踪子树中的节点数。
是否有比这更好的方法?
我在我的博客中发布了完整的解决方案和解释:
http://www.leetcode.com/2010/11/largest-binary-search-tree-bst-in.html
这个算法的思路是进行深度优先遍历,并从底部向上传递最小值和最大值,而不是自顶向下。换句话说,在验证上面的节点是否满足BST要求之前,我们会先验证更深的节点。
这样做的主要原因是当其中一个节点不满足BST属性时,所有上面的子树(包括该节点)也必须不满足BST要求。
与自顶向下的方法相比,自底向上的方法是一个非常好的选择,因为可以将子树的节点总数的结果传递给树上层。这样可以避免反复计算。子树的总节点数就是其左右子树的总节点数加一。
这个算法的时间复杂度为O(N),空间复杂度为O(1),效率非常高。(其中N是二叉树中的节点总数)。
以下是可行的C++代码。实现中的棘手部分是如何自底向上处理最小值和最大值。请注意,我没有将最小值和最大值初始化,因为它们在树的底部已经被初始化了。
// Find the largest BST subtree in a binary tree.
// If the subtree is a BST, return total number of nodes.
// If the subtree is not a BST, -1 is returned.
int findLargestBSTSubtree(BinaryTree *p, int &min, int &max,
int &maxNodes, BinaryTree *& largestBST) {
if (!p) return 0;
bool isBST = true;
int leftNodes = findLargestBSTSubtree(p->left, min, max, maxNodes, largestBST);
int currMin = (leftNodes == 0) ? p->data : min;
if (leftNodes == -1 ||
(leftNodes != 0 && p->data <= max))
isBST = false;
int rightNodes = findLargestBSTSubtree(p->right, min, max, maxNodes, largestBST);
int currMax = (rightNodes == 0) ? p->data : max;
if (rightNodes == -1 ||
(rightNodes != 0 && p->data >= min))
isBST = false;
if (isBST) {
min = currMin;
max = currMax;
int totalNodes = leftNodes + rightNodes + 1;
if (totalNodes > maxNodes) {
maxNodes = totalNodes;
largestBST = p;
}
return totalNodes;
} else {
return -1; // This subtree is not a BST
}
}
BinaryTree* findLargestBSTSubtree(BinaryTree *root) {
BinaryTree *largestBST = NULL;
int min, max;
int maxNodes = INT_MIN; // INT_MIN is defined in <climits>
findLargestBSTSubtree(root, min, max, maxNodes, largestBST);
return largestBST;
}
我猜你想要解决的问题是在二叉树中找到最大的(具有更多节点的)二叉搜索树。在这种情况下,您需要遍历所有树节点,检查它是否是一个二叉搜索树,一旦找到一个,您就需要检查它是否比此前找到的最大值更大。
class TreeNode
{
public int value;
public TreeNode left;
public TreeNode right;
}
void LargestBST(TreeNode bt, IDictionary<TreeNode, bool> isBST, IDictionary<TreeNode, int> nodeCount, ref TreeNode largestBST)
{
if (bt == null)
return;
if (IsBST(bt, isBST) && (largestBST == null || NodeCount(bt, nodeCount) > NodeCount(largestBST, nodeCount))
largestBST = bt;
else
{
LargestBST(bt.left, isBST, nodeCount, ref largestBST);
LargestBST(bt.Right, isBST, nodeCount, ref largestBST);
}
}
bool IsBST(TreeNode node, IDictionary<TreeNode, bool> isBST)
{
if (node == null)
return true;
bool result;
if (!isBST.TryGetValue(node, out result))
{
TreeNode maxLeft = Max(node.Left);
TreeNode minRight = Min(node.Right);
result = (maxLeft == null || maxLeft.value <= node.value) &&
(minRight == null || minRight.value >= node.value) &&
IsBST(node.left, isBST) && IsBST(node.Right, isBST);
isBST.Add(node, result);
}
return result;
}
TreeNode Max(TreeNode node)
{
if (node == null)
return null;
while (node.right != null)
node = node.right;
return node;
}
TreeNode Min(TreeNode node)
{
if (node == null)
return null;
while (node.left != null)
node = node.left;
return node;
}
int NodeCount(TreeNode node, IDictionary<TreeNode, int> nodeCount)
{
if (node == null)
return 0;
int result;
if (!nodeCount.TryGetValue(node, out result))
{
result = 1 + NodeCount(node.left, nodeCount) + NodeCount(node.right, nodeCount);
nodeCount.Add(node, result);
}
return result;
}
O(n)
空间,以便更轻松地分析和运行时。(一种对最小/最大值进行路径压缩的排序方法。) - Larry如果一棵树的中序遍历按顺序给出其元素,则该树是BST。如果您想要一个示例实现,可以使用此处的代码:http://placementsindia.blogspot.com/2007/12/c-program-to-check-whether-binary-tree.html
运行时间为O(N),其中N =节点数。
如果根节点的两个子树都是BST,则认为该树是BST是错误的(对于删除提出此解决方案的答案的人:您不应该删除您的答案,我个人不会对您进行投票,并且从一个看似好的但实际上不好的解决方案中学到的东西与从一个好的解决方案中学到的东西一样多)。反例:
3
/ \
2 4
/ \
1 5
现在,为了获得最大的二叉搜索子树,请考虑以下树:
3
/ \
2 4
/ \
1 5
10
/ \
2 14
/ \ |
1 5 20
我认为你可以通过自上而下的方式来避免检查每个节点是否是BST的根节点,而不是从底向上。如果一个子树是BST,它将比任何子树本身都要大,因此如果它已经通过了isBST测试,您就不需要在子树内部进行检查。然后,您只需使isBST返回有效树的大小,并将其与子树的根指针一起存储,如果您需要能够再次找到它,而不仅仅知道最大的树有多大。
更新:
这里发布的一些用于检查某些内容是否为BST的代码将失败一些情况,因为它们仅检查节点的父级。
例如:
10 / \ 4 99 / 2
这不是一个有效的二叉搜索树(BST),(2相对于10的位置不正确),但如果您不通过树向下发送最小值和最大值,您将错误地验证它为有效。这个伪代码考虑到了这一点。
main
{
Verify(root, MIN_VALUE, MAX_VALUE)
}
boolean Verify(node , min, max)
{
if(node == null)
return true;
if(node.value > min &&
node.value < max &&
Verify(node.leftchild, min, node.value) &&
Verify(node.rightchild,node.value,max)
{
return true;
}
else
{
return false;
}
}
int getMinMaxValue(Node* root, bool isMin)
{
if (!root)
{
// Not real limits...
return (isMin ? INT_MAX : INT_MIN);
}
int leftVal = getMinMaxValue(root->left, isMin);
int rightVal = getMinMaxValue(root->right, isMin);
if (isMin)
{
return min(root->value, min(leftVal, rightVal));
}
else
{
return max(root->value, max(leftVal, rightVal));
}
}
bool isBST(Node* root)
{
if (!root)
{
return true;
}
Node* left = root->left;
Node* right = root->right;
if (left)
{
if (getMinMaxValue(left, false) > root->value)
{
return false;
}
}
if (right)
{
if (getMinMaxValue(right, true) < root->value)
{
return false;
}
}
return isBST(left) && isBST(right);
}
然后从根节点开始下降,检查子树是否为BST,并取最大值。
public int MaxBST(Node root, int min, int max, ref Node maxbst,
ref int MaxNumNodes)
{
if (root == null) return 0;
//Not a BST
if (root.data < min || root.data > max) return -1;
//Find Max BST on left
int left = MaxBST(root.left, min, root.data, ref maxbst,
ref MaxNumNodes);
//Find Max BST on right
int right = MaxBST(root.right, root.data + 1, max, ref maxbst,
ref MaxNumNodes);
//Case1: -1 from both branches . No BST in both branches
if (left == -1 && right == -1) return -1;
//Case2:No BST in left branch , so choose right
//See if the BST on right is bigger than seen so far
if (left == -1)
{
if (right> MaxNumNodes)
{
MaxNumNodes = right;
maxbst = root.right;
}
return -1;
}
//Case3:No BST in right branch , so choose left
//See if the BST on left is bigger than seen so far
if (right == -1)
{
if (left > MaxNumNodes)
{
MaxNumNodes = left;
maxbst = root.left;
}
return -1;
}
//Case4:Both are BST , new max is left BST + right BST
maxbst = root;
return left + right + 1;
}
int maxNodes = INT.MIN;
Node* lb = NULL;
int largesBST(Node* root) {
largesBST(root, INT.MIN, INT.MAX);
}
int largesBST(Node* p, int MIN, int MAX) {
if(!p) { return 0; }
if(p->data > MIN || p->data < MAX) { return -1; }
int lc = largestBST(p->left, p->data, MAX);
if(lc == -1) { return -1; }
int rc = largestBST(p->right, MIN, p->data);
if(rc == -1) { return -1; }
// At this point, cur node is BST
int curNodes = lc + rc + 1;
if(curNodes > maxNodes) {
maxNodes = curNodes;
lb = p;
}
}
这不是最优的方法,但您可以对二叉树进行中序遍历并将其存储在数组中,然后找到最长的连续递增序列,这将给出具有最大节点数的BST。复杂度为O(n)
用于遍历和O(n)
用于搜索,因此仍将是O(n)
。