找到BST中最大的子树

时间:2010-02-25 17:31:18

标签: algorithm tree binary-tree

给定一个二叉树,我想找出其中最大的子树BST。

天真的方法:

我有一个天真的方法,我访问树的每个节点,并将此节点传递给isBST函数。如果它是BST,我还将跟踪子树中的节点数。

有比这更好的方法吗?

9 个答案:

答案 0 :(得分:12)

我在博客中发布了完整的解决方案和解释:

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;
}

答案 1 :(得分:8)

我想你要解决的问题是找到BT中最大的(有更多节点)BST。在这种情况下,您需要遍历所有树节点,检查它是否是BST,并且一旦找到它,您将不得不检查它是否有比当前发现的最大节点更多的节点。

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;
}

答案 2 :(得分:2)

如果树的顺序遍历以排序顺序为您提供元素,则该树是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

现在,要获得最大的BST子树,请考虑以下树:

    3
   / \
  2   4
 / \
1  5

inorder-traversal是1 2 5 3 4.我认为你可以通过在inorder-traversal中找到最大长度排序的连续子序列来解决原始问题。您必须小心不要选择不描述BST的序列。例如,对于:

    10
   / \
  2   14
 / \  |
1  5  20

inorder-traversal是1 2 5 10 20 14.不要选择20.我认为这可以通过确保只要他们的选择停止有意义就解雇元素来实现。例如,当你达到14时,忽略20.我不确定这是否可以有效地完成。如果我找到一个确切的方法,我会编辑我的帖子。

答案 3 :(得分:1)

我认为你可以通过自上而下而不是自下而上来避免检查每个节点是否是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;
  }
}

答案 4 :(得分:1)

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,并取最大值。

答案 5 :(得分:0)

要验证节点是否是BST的根,我们必须递归检查每个左右子节点。如果从root开始,则必须重新生成所有子项,然后才能确定二叉树的根是否为BST。因此,为每个节点调用“isBST”没有任何意义。这是我的方法

  1. 从根开始
  2. 从左右找到最大值
  3. 如果不是左右最大返回 “不是BST”
  4. 如果BST在左边,检查是否是 到目前为止大于最大值。如是 存储它并返回“NOT BST”
  5. 如果BST在右边,检查是否是 到目前为止大于最大值。如是 存储它并返回“NOT BST”
  6. 如果左右是BST,那就是 以当前根为ROOT的新BST 并且剩余节点数+ 对+ 1
  7. 使用这个工作的几个挑战是存储max到目前为止我使用了ref变量MaxNumNodes。 maxbst withh具有函数返回时找到的最大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;
    
        }
    

答案 6 :(得分:0)

这不是最优化的方法,但您可以对二叉树进行顺序遍历并将其存储在数组中,然后找到最长的连续增加序列,这将为您提供具有最大节点数的BST。遍历的复杂度为O(n),搜索的复杂度为O(n),因此它仍然是O(n)

答案 7 :(得分:0)

一种可能的解决方案如下 -

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;
     }
}

答案 8 :(得分:0)

解决上述问题:

  1. 可以对树进行按顺序遍历
  2. 将其存储在数组中,找到“最大的已排序子集”。