1. 程式人生 > >[LeetCode] Largest BST Subtree 最大的二分搜尋子樹

[LeetCode] Largest BST Subtree 最大的二分搜尋子樹

Given a binary tree, find the largest subtree which is a Binary Search Tree (BST), where largest means subtree with largest number of nodes in it.

Note:
A subtree must include all of its descendants.
Here's an example:

    10
    / \
   5  15
  / \   \ 
 1   8   7

The Largest BST Subtree in this case is the highlighted one. 
The return value is the subtree's size, which is 3.

Hint:

  1. You can recursively use algorithm similar to 98. Validate Binary Search Tree at each node of the tree, which will result in O(nlogn) time complexity.

Follow up:
Can you figure out ways to solve it with O(n) time complexity?

這道題讓我們求一棵二分樹的最大二分搜尋子樹,所謂二分搜尋樹就是滿足左<根<右的二分樹,我們需要返回這個二分搜尋子樹的節點個數。題目中給的提示說我們可以用之前那道

Validate Binary Search Tree的方法來做,時間複雜度為O(n2),這種方法是把每個節點都當做根節點,來驗證其是否是二叉搜尋數,並記錄節點的個數,若是二叉搜尋樹,就更新最終結果,參見程式碼如下:

解法一:

class Solution {
public:
    int largestBSTSubtree(TreeNode* root) {
        int res = 0;
        dfs(root, res);
        return res;
    }
    void dfs(TreeNode *root, int &res) {
        
if (!root) return; int d = countBFS(root, INT_MIN, INT_MAX); if (d != -1) { res = max(res, d); return; } dfs(root->left, res); dfs(root->right, res); } int countBFS(TreeNode *root, int mn, int mx) { if (!root) return 0; if (root->val <= mn || root->val >= mx) return -1; int left = countBFS(root->left, mn, root->val); if (left == -1) return -1; int right = countBFS(root->right, root->val, mx); if (right == -1) return -1; return left + right + 1; } };

下面我們來看一種更簡潔的寫法,對於每一個節點,都來驗證其是否是BST,如果是的話,我們就統計節點的個數即可,參見程式碼如下:

解法二:

class Solution {
public:
    int largestBSTSubtree(TreeNode* root) {
        if (!root) return 0;
        if (isValid(root, INT_MIN, INT_MAX)) return count(root);
        return max(largestBSTSubtree(root->left), largestBSTSubtree(root->right));
    }
    bool isValid(TreeNode* root, int mn, int mx) {
        if (!root) return true;
        if (root->val <= mn || root->val >= mx) return false;
        return isValid(root->left, mn, root->val) && isValid(root->right, root->val, mx);
    }
    int count(TreeNode* root) {
        if (!root) return 0;
        return count(root->left) + count(root->right) + 1;
    }
};

題目中的Follow up讓我們用O(n)的時間複雜度來解決問題,我們還是採用DFS的思想來解題,由於時間複雜度的限制,只允許我們遍歷一次整個二叉樹,由於滿足題目要求的 二叉搜尋子樹必定是有葉節點的,所以我們的思路就是先遞迴到最左子節點,然後逐層往上遞迴,對於每一個節點,我們都記錄當前最大的BST的節點數,當做為左子樹的最大值,和做為右子樹的最小值,當每次遇到左子節點不存在或者當前節點值大於左子樹的最大值,且右子樹不存在或者當前節點值小於右子樹的最小數時,說明BST的節點數又增加了一個,我們更新結果及其引數,如果當前節點不是BST的節點,那麼我們更新BST的節點數res為左右子節點的各自的BST的節點數的較大值,參見程式碼如下:

解法三:

class Solution {
public:
    int largestBSTSubtree(TreeNode* root) {
        int res = 0, mn = INT_MIN, mx = INT_MAX;
        bool d = isValidBST(root, mn, mx, res);
        return res;
    }
    bool isValidBST(TreeNode *root, int &mn, int &mx, int &res) {
        if (!root) return true;
        int left_n = 0, right_n = 0, left_mn = INT_MIN;
        int right_mn = INT_MIN, left_mx = INT_MAX, right_mx = INT_MAX;
        bool left = isValidBST(root->left, left_mn, left_mx, left_n);
        bool right = isValidBST(root->right, right_mn, right_mx, right_n);
        if (left && right) {
            if ((!root->left || root->val > left_mx) && (!root->right || root->val < right_mn)) {
                res = left_n + right_n + 1;
                mn = root->left ? left_mn : root->val;
                mx = root->right ? right_mx : root->val;
                return true;
            }
        }
        res = max(left_n, right_n);
        return false;
    }
};

類似題目:

參考資料: