1. 程式人生 > >找出二叉樹中最大的子樹,且子樹為二叉搜尋樹

找出二叉樹中最大的子樹,且子樹為二叉搜尋樹

題目

找出二叉樹中最大的子樹,該子樹為二叉搜尋樹。所謂最大的子樹就是指結點數目最多的子樹。

分析

該題目是要找出二叉樹中最大的子樹,該子樹必須是二叉搜尋樹(BST)。子樹的概念需要重點關注一下,以下面一棵二叉樹為例
         ____10____
        /          \
      __5_         15_
     /    \           \
     1     8           7
那麼該二叉樹最大的為BST的子樹應該算subtree(1)還是subtree(2)呢?
         ____ 10____
        /           \
      __5
_ 15 -------- subtree (1)      / \ 1 8 
      __5_
     /    \               -------- subtree (2)
     1     8 
根據維基百科對子樹的定義,一棵二叉樹T的子樹由T的某個結點和該結點所有的後代構成。也就是說,該題目中,subtree(2)才是正確的答案,因為subtree(1)不包含結點7,不滿足子樹的定義。

基本解法—自頂向下

最自然的解法是以根結點開始遍歷二叉樹所有的結點,判定以當前結點為根的子樹是否是BST,如果是,則該結點為根的BST就是最大的BST。如果不是,遞迴呼叫左右子樹,返回其中包含較多結點的子樹。
void maxSubTree(pNode root, int &max, pNode &ret)
{
    if (!root) {
        max = 0;
        ret = root;
        return;
    }
    if (isBST(root)) { //以root為根結點的樹為BST,則設定結果為root並返回。該函式請參看博文《判定一顆樹是否是二叉搜尋樹》
        max = size(root);
        ret = root;
        return;
    }
    int lmax, rmax;
    pNode lret, rret;

    maxSubTree(root->left, lmax, lret);   //找出左子樹中為BST的最大的子樹
    maxSubTree(root->right, rmax, rret);  //找出右子樹中為BST的最大的子樹
    max = lmax > rmax ? lmax : rmax;      //設定結點最大數目
    ret = lmax > rmax ? lret : rret;      //設定為BST的最大子樹的根結點
}



優化方法—自底向上

由於自頂向下的方法每次都要呼叫isBST來判斷當前結點為根結點的子樹是否是二叉搜尋樹,每次呼叫時間為O(n),其實這裡面有些重複的判斷。如果採用自底向上的方法,我們在判斷上面結點為根的子樹是否是BST之前已經知道底部結點為根的子樹是否是BST。因此只要以底部結點為根的子樹不是BST,則以它上面結點為根的子樹一定不是BST。

判定一顆樹是否是BST的方法如下:

1)每個結點的左右子樹都是BST

2)每個結點的值大於左子樹的最大值

3)每個結點的值小於右子樹的最小值

因此採用自底向上的方法時,我們需要向上傳遞一些資訊,包括子樹的最大值和最小值以及子樹的大小。顯然,樹的大小=左子樹大小+右子樹大小+1。

// 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;
  findLargestBSTSubtree(root, min, max, maxNodes, largestBST);
  return largestBST;
}


 參考