Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

549. Binary Tree Longest Consecutive Sequence II - Leetcode Solution

Problem Description

Given the root of a binary tree, you are asked to find the length of the longest path in the tree such that each consecutive node in the path has a value that is either one greater or one less than its parent. This means you are looking for the longest consecutive increasing or decreasing sequence, and the path can move both up and down the tree (not just parent-to-child as in some other problems).

The path may start and end at any node in the tree, but each consecutive pair of nodes in the path must be directly connected (i.e., parent-child relationship), and the values must be consecutive (difference of exactly 1, either increasing or decreasing).

  • The path does not need to pass through the root.
  • Each node can only be used once in the path.
  • The path must be made of connected nodes (no skipping).

Your task is to return the length of the longest such consecutive path.

Thought Process

At first glance, the problem seems similar to finding the longest consecutive path, but with the added twist that the path can go both increasing and decreasing, and can go up and down the tree (not just one direction). A brute-force approach might be to try every possible path, but this would be extremely inefficient for large trees.

Instead, we want to find a way to efficiently compute, for each node, the longest increasing and decreasing consecutive sequences that pass through it, and combine them to get the answer. This suggests a recursive, post-order traversal approach: for each node, check its children, and compute the longest increasing and decreasing sequences that can be extended through the current node.

The key insight is that at each node, the longest consecutive path passing through it is the sum of the longest increasing and decreasing paths from its children (in opposite directions), minus one to avoid counting the current node twice.

Solution Approach

To solve this problem efficiently, we use a recursive post-order traversal. At each node, we calculate two values:

  • inc: The length of the longest increasing consecutive sequence starting at this node.
  • dec: The length of the longest decreasing consecutive sequence starting at this node.

Here is how we proceed:

  1. For each node, recursively compute the inc and dec values for its left and right children.
  2. If the child exists and its value is one more than the current node's value, we can extend the decreasing sequence (dec).
  3. If the child exists and its value is one less than the current node's value, we can extend the increasing sequence (inc).
  4. At each node, the longest consecutive path that passes through it is inc + dec - 1 (since the current node is counted in both inc and dec).
  5. We keep track of the global maximum as we traverse the tree.

This approach ensures that each node is visited only once, and the computation at each node is constant time.

Example Walkthrough

Consider the following binary tree:

        2
       / \
      1   3
         / \
        2   4
  

Let's trace the computation:

  • At node 2 (left child of root): No children, so inc=1, dec=1.
  • At node 2 (right child of 3): No children, so inc=1, dec=1.
  • At node 4: No children, so inc=1, dec=1.
  • At node 3:
    • Left child 2: Since 2 is one less than 3, we can extend the decreasing sequence: dec = left.dec + 1 = 2.
    • Right child 4: Since 4 is one more than 3, we can extend the increasing sequence: inc = right.inc + 1 = 2.
    • So for node 3, inc=2, dec=2, so the path passing through node 3 is inc+dec-1 = 3.
  • At root node 2:
    • Left child 1: 1 is one less than 2, so dec = left.dec + 1 = 2.
    • Right child 3: 3 is one more than 2, so inc = right.inc + 1 = 3.
    • So for root 2, inc=3, dec=2, path length = 4.

The longest consecutive path is 4 (1-2-3-4).

Time and Space Complexity

Brute-force Approach:
If we tried all possible paths, the time complexity would be exponential, as each node could be a starting point and paths could overlap, leading to a huge number of possibilities.

Optimized Approach:
The recursive solution visits each node exactly once, and at each node performs constant work, so the time complexity is O(N), where N is the number of nodes in the tree.

The space complexity is O(H) due to the recursion stack, where H is the height of the tree (worst case O(N) for a skewed tree).

Summary

The key to solving the Binary Tree Longest Consecutive Sequence II problem is recognizing that the path can go both up and down, and can increase or decrease by 1 at each step. By using a recursive post-order traversal and tracking the longest increasing and decreasing sequences at each node, we efficiently compute the solution in linear time. The elegance of the approach lies in combining the results from both children at each node and updating the global maximum accordingly.

Code Implementation

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def longestConsecutive(self, root: TreeNode) -> int:
        self.maxlen = 0
        
        def dfs(node):
            if not node:
                return (0, 0)  # (inc, dec)
            inc = dec = 1
            if node.left:
                left_inc, left_dec = dfs(node.left)
                if node.left.val == node.val + 1:
                    inc = max(inc, left_inc + 1)
                elif node.left.val == node.val - 1:
                    dec = max(dec, left_dec + 1)
            if node.right:
                right_inc, right_dec = dfs(node.right)
                if node.right.val == node.val + 1:
                    inc = max(inc, right_inc + 1)
                elif node.right.val == node.val - 1:
                    dec = max(dec, right_dec + 1)
            self.maxlen = max(self.maxlen, inc + dec - 1)
            return (inc, dec)
        
        dfs(root)
        return self.maxlen
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxlen = 0;
    pair dfs(TreeNode* node) {
        if (!node) return {0, 0}; // {inc, dec}
        int inc = 1, dec = 1;
        if (node->left) {
            auto left = dfs(node->left);
            if (node->left->val == node->val + 1)
                inc = max(inc, left.first + 1);
            else if (node->left->val == node->val - 1)
                dec = max(dec, left.second + 1);
        }
        if (node->right) {
            auto right = dfs(node->right);
            if (node->right->val == node->val + 1)
                inc = max(inc, right.first + 1);
            else if (node->right->val == node->val - 1)
                dec = max(dec, right.second + 1);
        }
        maxlen = max(maxlen, inc + dec - 1);
        return {inc, dec};
    }
    int longestConsecutive(TreeNode* root) {
        dfs(root);
        return maxlen;
    }
};
      
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private int maxlen = 0;
    public int longestConsecutive(TreeNode root) {
        dfs(root);
        return maxlen;
    }
    private int[] dfs(TreeNode node) {
        if (node == null) return new int[]{0, 0}; // {inc, dec}
        int inc = 1, dec = 1;
        if (node.left != null) {
            int[] left = dfs(node.left);
            if (node.left.val == node.val + 1)
                inc = Math.max(inc, left[0] + 1);
            else if (node.left.val == node.val - 1)
                dec = Math.max(dec, left[1] + 1);
        }
        if (node.right != null) {
            int[] right = dfs(node.right);
            if (node.right.val == node.val + 1)
                inc = Math.max(inc, right[0] + 1);
            else if (node.right.val == node.val - 1)
                dec = Math.max(dec, right[1] + 1);
        }
        maxlen = Math.max(maxlen, inc + dec - 1);
        return new int[]{inc, dec};
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var longestConsecutive = function(root) {
    let maxlen = 0;
    function dfs(node) {
        if (!node) return [0, 0]; // [inc, dec]
        let inc = 1, dec = 1;
        if (node.left) {
            let [leftInc, leftDec] = dfs(node.left);
            if (node.left.val === node.val + 1)
                inc = Math.max(inc, leftInc + 1);
            else if (node.left.val === node.val - 1)
                dec = Math.max(dec, leftDec + 1);
        }
        if (node.right) {
            let [rightInc, rightDec] = dfs(node.right);
            if (node.right.val === node.val + 1)
                inc = Math.max(inc, rightInc + 1);
            else if (node.right.val === node.val - 1)
                dec = Math.max(dec, rightDec + 1);
        }
        maxlen = Math.max(maxlen, inc + dec - 1);
        return [inc, dec];
    }
    dfs(root);
    return maxlen;
};