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).
Your task is to return the length of the longest such consecutive path.
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.
To solve this problem efficiently, we use a recursive post-order traversal. At each node, we calculate two values:
Here is how we proceed:
inc
and dec
values for its left and right children.
dec
).
inc
).
inc + dec - 1
(since the current node is counted in both inc
and dec
).
This approach ensures that each node is visited only once, and the computation at each node is constant time.
Consider the following binary tree:
2 / \ 1 3 / \ 2 4
Let's trace the computation:
The longest consecutive path is 4 (1-2-3-4).
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).
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.
# 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;
};