1. 程式人生 > >[LeetCode] Second Minimum Node In a Binary Tree 二叉樹中第二小的結點

[LeetCode] Second Minimum Node In a Binary Tree 二叉樹中第二小的結點

Given a non-empty special binary tree consisting of nodes with the non-negative value, where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, then this node's value is the smaller value among its two sub-nodes.

Given such a binary tree, you need to output the second minimum value in the set made of all the nodes' value in the whole tree.

If no such second minimum value exists, output -1 instead.

Example 1:

Input: 
    2
   / \
  2   5
     / \
    5   7

Output: 5
Explanation: The smallest value is 2, the second smallest value is 5.

Example 2:

Input: 
    2
   / \
  2   2

Output: -1
Explanation: The smallest value is 2, but there isn't any second smallest value.

這道題讓我們找二叉樹中的第二小的結點值,並且給該二叉樹做了一些限制,比如對於任意一個結點,要麼其沒有子結點,要麼就同時有兩個子結點,而且父結點值是子結點值中較小的那個,當然兩個子結點值可以相等。那麼直接上暴力搜尋唄,根據該樹的附加條件可知,根結點一定是最小的結點值first,那麼我們只要找出第二小的值second即可,初始化為整型的最大值。然後對根結點呼叫遞迴函式,將first和second當作引數傳進去即可。在遞迴函式中,如果當前結點為空,直接返回,若當前結點孩值不等於first,說明其肯定比first要大,然後我們看其是否比second小,小的話就更新second,然後對當前結點的左右子結點分別呼叫遞迴函式即可,參見程式碼如下:

解法一:

class Solution {
public:
    int findSecondMinimumValue(TreeNode* root) {
        int first = root->val, second = INT_MAX;
        helper(root, first, second);
        return (second == first || second == INT_MAX) ? -1 : second;
    }
    void helper(TreeNode* node, int& first, int& second) {
        if (!node) return;
        if (node->val != first && node->val < second) {
            second = node->val;
        }
        helper(node->left, first, second);
        helper(node->right, first, second);
    }
};

下面這種方法也是用遞迴來做的,不過現在遞迴函式有了返回值,在遞迴函式中,還是先判斷當前結點是否為空,為空直接返回-1。然後就是看當前結點是否等於first,不等於直接返回當前結點值。如果等於,我們對其左右子結點分別呼叫遞迴函式,分別得到left和right。如果left和right其中有一個為-1了,我們取其中的較大值;如果left和right都不為-1,我們取其中的較小值返回即可,參見程式碼如下:

解法二:

class Solution {
public:
    int findSecondMinimumValue(TreeNode* root) {
        return helper(root, root->val);
    }
    int helper(TreeNode* node, int first) {
        if (!node) return -1;
        if (node->val != first) return node->val;
        int left = helper(node->left, first), right = helper(node->right, first);
        return (left == -1 || right == -1) ? max(left, right) : min(left, right);
    }
};

下面這種遞迴方法更加簡潔了,沒有再使用專門的遞迴函式helper,而是對當前根結點判斷其左子樹是否存在,不存在就返回-1。題目中說了是非空樹,所以根結點一定存在。然後我們比較如果左子結點值等於根結點值,我們則對其左子結點呼叫遞迴函式;否則left就等於其左子結點值。再比較如果右子結點值等於根結點值,則對其右子結點呼叫遞迴函式;否則right就等於其右子結點值。最後我們還是看如果left和right其中有一個為-1了,我們取其中的較大值;如果left和right都不為-1,我們取其中的較小值返回即可,參見程式碼如下:

解法三:

class Solution {
public:
    int findSecondMinimumValue(TreeNode* root) {
        if (!root->left) return -1;
        int left = (root->left->val == root->val) ? findSecondMinimumValue(root->left) : root->left->val;
        int right = (root->right->val == root->val) ? findSecondMinimumValue(root->right) : root->right->val;
        return (left == -1 || right == -1) ? max(left, right) : min(left, right);
    }
};

整了三種遞迴的解法,來看一種迭代的解法吧,用的是層序遍歷,但還是用的解法一種的不停更新second的方法,參見程式碼如下:

解法四:

class Solution {
public:
    int findSecondMinimumValue(TreeNode* root) {
        int first = root->val, second = INT_MAX;
        queue<TreeNode*> q{{root}};
        while (!q.empty()) {
            auto t = q.front(); q.pop();
            if (t->val != first && t->val < second) {
                second = t->val;
            }
            if (t->left) q.push(t->left);
            if (t->right) q.push(t->right);
        }
        return (second == first || second == INT_MAX) ? -1 : second;
    }
};

類似題目:

參考資料: