1. 程式人生 > >[LeetCode] Binary Tree Longest Consecutive Sequence II 二叉樹最長連續序列之二

[LeetCode] Binary Tree Longest Consecutive Sequence II 二叉樹最長連續序列之二

Given a binary tree, you need to find the length of Longest Consecutive Path in Binary Tree.

Especially, this path can be either increasing or decreasing. For example, [1,2,3,4] and [4,3,2,1] are both considered valid, but the path [1,2,4,3] is not valid. On the other hand, the path can be in the child-Parent-child order, where not necessarily be parent-child order.

Example 1:

Input:
        1
       / \
      2   3
Output: 2
Explanation: The longest consecutive path is [1, 2] or [2, 1].

Example 2:

Input:
        2
       / \
      1   3
Output: 3
Explanation: The longest consecutive path is [1, 2, 3] or [3, 2, 1].

Note: All the values of tree nodes are in the range of [-1e7, 1e7].

這道題是之前那道Binary Tree Longest Consecutive Sequence的拓展,那道題只讓從父結點到子結點這種順序來找最長連續序列,而這道題沒有這個順序限制,我們可以任意的拐彎,這樣能找到最長的遞增或者遞減的路徑。這道題利用回溯的思想比較容易,因為當一個結點沒有子結點點時,它只需要跟其父結點進行比較,這種情況最容易處理,而且一旦葉結點處理完了,我們可以一層一層的回溯,直到回到根結點,然後再遍歷的過程中不斷的更新結果res即可。由於題目中說了要麼是遞增,要麼是遞減,我們不能一會遞增一會遞減,所以我們遞增遞減的情況都要統計,只是最後取最長的路徑。所以我們要知道每一個結點的最長遞增和遞減路徑的長度,當然是從葉結點算起,這樣才方便往根結點回溯。當某個結點比其父結點值大1的話,說明這條路徑是遞增的,那麼當我們知道其左右子結點各自的遞增路徑長度,那麼當前結點的遞增路徑長度就是左右子結點遞增路徑長度中的較大值加上1,同理如果是遞減路徑,那麼當前結點的遞減路徑長度就是左右子結點遞減路徑長度中的較大值加上1,通過這種方式我們可以更新每個結點的遞增遞減路徑長度。在回溯的過程中,一旦我們知道了某個結點的左右子結點的最長遞增遞減路徑長度,那麼我們可以算出當前結點的最長連續序列的長度,要麼是左子結點的遞增路徑跟右子結點的遞減路徑之和加1,要麼事左子結點的遞減路徑跟右子結點的遞增路徑之和加1,二者中取較大值即可,參見程式碼如下:

解法一:

class Solution {
public:
    int longestConsecutive(TreeNode* root) {
        int res = 0;
        helper(root, root, res);
        return res;
    }
    pair<int, int> helper(TreeNode* node, TreeNode* parent, int& res) {
        if (!node) return {0, 0};
        auto left = helper(node->left, node, res);
        auto right = helper(node->right, node, res);
        res = max(res, left.first + right.second + 1);
        res = max(res, left.second + right.first + 1);
        int inc = 0, dec = 0;
        if (node->val == parent->val + 1) {
            inc = max(left.first, right.first) + 1;
        } else if (node->val + 1 == parent->val) {
            dec = max(left.second, right.second) + 1;
        }
        return {inc, dec};
    }
};

上面的方法把所有內容都寫到了一個遞迴函式中,看起來有些臃腫。而下面這種方法分了兩個遞迴來寫,相對來說簡潔一些。因為每個結點的最長連續序列長度等於其最長遞增路徑長度跟最長遞減路徑之和加1,然後分別對其左右子結點呼叫遞迴函式,取三者最大值,相當於對二叉樹進行了先序遍歷,參見程式碼如下:

解法二:

class Solution {
public:
    int longestConsecutive(TreeNode* root) {
        if (!root) return 0;
        int res = helper(root, 1) + helper(root, -1) + 1;
        return max(res, max(longestConsecutive(root->left), longestConsecutive(root->right)));
    }
    int helper(TreeNode* node, int diff) {
        if (!node) return 0;
        int left = 0, right = 0;
        if (node->left && node->val - node->left->val == diff) {
            left = 1 + helper(node->left, diff);
        }
        if (node->right && node->val - node->right->val == diff) {
            right = 1 + helper(node->right, diff);
        }
        return max(left, right);
    }
};

類似題目:

參考資料: