298. Binary Tree Longest Consecutive Sequence
Problem Description
The problem specifies a binary tree and requires us to find the length of the longest consecutive sequence path within it. A consecutive sequence path is defined as a path in which adjacent nodes in the path have values that differ by exactly one. An important aspect of this problem is that the path does not need to begin at the root of the tree; it can start at any node. Additionally, the path is uni-directional, meaning once you move from a parent node to a child node, you cannot move back up to the parent.
Flowchart Walkthrough
To analyze LeetCode problem 298, Binary Tree Longest Consecutive Sequence, using the Flowchart, we can follow these steps in the decision-making process:
-
Is it a graph?
- Yes: A binary tree is a type of graph with nodes (the values in the tree) and edges between parent and child nodes.
-
Is it a tree?
- Yes: Clearly, as indicated in the problem, we are dealing with a binary tree.
-
Using Depth-First Search (DFS)
- For this type of problem where we are looking to explore depth-first to search for the longest consecutive sequence from any given node, DFS is an applicable and efficient method. DFS allows us to explore each branch of the tree to its fullest depth before backtracking, which is ideal for this type of problem where sequences need to be explored in their entirety along each path.
Conclusion: By using the flowchart, we find that applying DFS is suitable for solving the Binary Tree Longest Consecutive Sequence problem, as it deals with depth exploration in tree structures.
Intuition
To solve this problem, we can use a Depth-First Search (DFS) approach. The idea is to explore each node starting from the root and recursively check its children to find the longest increasing consecutive sequence that includes the node itself as either the start or some part of the sequence. The DFS approach allows us to go as deep as possible down each path before backtracking, which is necessary to examine all potential paths.
Specifically, at each node, we will perform the following actions:
- We check if the node is
None
. If it is, there's no sequence to be found, so we return0
. - We recursively call the
dfs
function on the left and right children. The returned values represent the lengths of the consecutive sequences that start with the children. - We increment the lengths by one to include the current node. At this point, the length assumes that the current node is part of the sequence.
- We need to check if the child node actually follows the consecutive sequence. We do this by ensuring that the difference between the value of the child node and the current node is exactly one. If not, we reset the length to
1
as the sequence can only start from the current node. - We calculate the temporary longest sequence
t
that can be created by the current node with its children by taking the maximum of the lengths from its left and right child sequences. - We maintain a global variable
ans
that keeps track of the longest sequence found so far. After processing each node, we updateans
with the maximum value betweenans
andt
.
In the end, we return ans
, which will contain the maximum length of the longest consecutive sequence path found anywhere in the binary tree.
Learn more about Tree, Depth-First Search and Binary Tree patterns.
Solution Approach
To implement the solution approach for finding the longest consecutive sequence path in a binary tree, the Depth-First Search (DFS) algorithm is used, utilizing recursion. The code defines a helper function dfs
within the Solution
class's longestConsecutive
method to perform the DFS.
Here's how the implementation works step by step:
-
The
dfs
function is defined to take aTreeNode
as an input and returns an integer which is the length of the longest consecutive path that can be formed including the given node. If the node isNone
, the function returns 0, indicating that a null node cannot contribute to the length of a sequence. -
Invoking
dfs
recursively, we analyze the left and right subtrees by callingdfs(root.left)
anddfs(root.right)
. The returned values from both calls represent the lengths of consecutive sequence paths starting at the left and right children, respectively. -
A value of
1
is added to both lengths to include the currentroot
in the sequence. This is based on the assumption that the currentroot
extends the paths from its children by one. -
Two
if
conditions ensure that the sequence is indeed consecutive. If the left child's value is not one more than the current node's value or if the right child's value is not one more than the current node's value, the corresponding sequence length is reset to1
, indicating that a new sequence can start atroot
. -
The temporary maximum length
t
is determined by taking the maximum of the adjusted values from the left and right sequences. -
The non-local variable
ans
is used to keep track of the global maximum. This variable is updated (if necessary) every time a new local maximum is found, ensuring it always holds the length of the longest consecutive sequence seen so far. -
After updating
ans
,t
is returned, referring to the longest sequence that includes the currentroot
.
The recursive DFS strategy allows each node to contribute to the sequential path in a bottom-up approach, combining individual paths from leaf nodes to the root (or any other starting node), updating the global maximum along the way.
In the main function longestConsecutive
, ans
is initialized to 0, then the recursive dfs
process is kickstarted by passing the root
node. After exploring all possible nodes, the final ans
represents the solution and is returned.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's assume we have the following binary tree structure to help illustrate the solution approach:
1 / \ 2 3 / \ 4 2 / 3
We need to find the length of the longest consecutive sequence path in this tree.
Here's a step-by-step walkthrough with our sample tree:
-
Start with the root node (1). The
dfs
function is called with node1
as input. -
Call
dfs
recursively for the left child (dfs(2)
) and right child (dfs(3)
). -
In
dfs(2)
, it recursive callsdfs(4)
.dfs(4)
returns1
because it is a leaf node and has no children.- Since
4
is one more than2
, we increase the sequence length to2
(initial1
, plus 1 since it's consecutive). - This sequence length is returned to the
dfs
call on node2
.
-
For the right child
dfs(3)
of root node1
:dfs(3)
callsdfs(2)
, which then callsdfs(3)
on its own left child.- The left child
3
is a leaf and returns1
. - The node
2
has a child with a value of one less than itself, so this is not an incrementing consecutive sequence. dfs(2)
resets the sequence length to1
.
-
Now we compare the temporary lengths from left and right children of the root. For the left side, we got a length of
2
(from1->2->4
sequence), and from the right side, we have a length of1
(since the1->3->2
sequence isn't increasing consecutively). -
The temporary maximum
t
at root node1
is2
(the maximum of2
and1
). However, root1
does not increment either sequence, so we do not add1
to it. -
We update the global maximum
ans
ift
is greater than the currentans
. For the root node,ans
is updated to2
. -
dfs(1)
returnst
, which is2
.
Hence after running our dfs
algorithm on the binary tree, we find that the longest consecutive sequence path has a length of 2
from the sequence 1->2->4
.
Solution Implementation
1# Definition for a binary tree node.
2class TreeNode:
3 def __init__(self, val=0, left=None, right=None):
4 self.val = val
5 self.left = left
6 self.right = right
7
8class Solution:
9 def longestConsecutive(self, root: Optional[TreeNode]) -> int:
10 # Helper function that uses depth-first search to find the longest consecutive sequence.
11 def dfs(node: Optional[TreeNode]) -> int:
12 if node is None:
13 return 0
14 left_length = dfs(node.left) + 1 # Length of the sequence from the left child
15 right_length = dfs(node.right) + 1 # Length of the sequence from the right child
16
17 # If the left child is not consecutive, reset the length for that path
18 if node.left and node.left.val - node.val != 1:
19 left_length = 1
20
21 # If the right child is not consecutive, reset the length for that path
22 if node.right and node.right.val - node.val != 1:
23 right_length = 1
24
25 # The longest path through the current node is the larger of its two children's paths
26 current_max_length = max(left_length, right_length)
27
28 # Update the global answer if the current path is longer than the previously found paths
29 nonlocal max_length
30 max_length = max(max_length, current_max_length)
31
32 # Return the length of the longest path passing through this node
33 return current_max_length
34
35 max_length = 0 # Initialize the maximum length variable
36 dfs(root) # Start DFS from the root of the tree
37 return max_length # Return the longest consecutive sequence length found
38
1/**
2 * Definition for a binary tree node.
3 */
4class TreeNode {
5 int val;
6 TreeNode left;
7 TreeNode right;
8 TreeNode() {}
9 TreeNode(int val) { this.val = val; }
10 TreeNode(int val, TreeNode left, TreeNode right) {
11 this.val = val;
12 this.left = left;
13 this.right = right;
14 }
15}
16
17class Solution {
18 private int longestStreak;
19
20 /**
21 * Finds the length of the longest consecutive sequence path in a binary tree.
22 * @param root The root of the binary tree.
23 * @return The length of the longest consecutive sequence.
24 */
25 public int longestConsecutive(TreeNode root) {
26 dfs(root);
27 return longestStreak;
28 }
29
30 /**
31 * Depth-first search helper method that traverses the tree and finds the longest consecutive path.
32 * The current node's value must be exactly one more than its parent's value to be part of the sequence.
33 * @param node The current node in the DFS traversal.
34 * @return The length of the longest path ending at the current node.
35 */
36 private int dfs(TreeNode node) {
37 if (node == null) {
38 return 0;
39 }
40
41 int leftLength = dfs(node.left) + 1; // Length of left child sequence + current node
42 int rightLength = dfs(node.right) + 1; // Length of right child sequence + current node
43
44 // If the left child's value is not consecutive, reset left sequence length
45 if (node.left != null && node.left.val - node.val != 1) {
46 leftLength = 1;
47 }
48
49 // If the right child's value is not consecutive, reset right sequence length
50 if (node.right != null && node.right.val - node.val != 1) {
51 rightLength = 1;
52 }
53
54 // The maximum length of the consecutive sequence at the current node
55 int currentMax = Math.max(leftLength, rightLength);
56
57 // Update the longest streak if the current max is greater
58 longestStreak = Math.max(longestStreak, currentMax);
59
60 return currentMax;
61 }
62}
63
1#include <functional> // We should include the necessary header for std::function
2
3// Definition for a binary tree node.
4struct TreeNode {
5 int val;
6 TreeNode *left;
7 TreeNode *right;
8 TreeNode() : val(0), left(nullptr), right(nullptr) {}
9 TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
10 TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
11};
12
13class Solution {
14public:
15 // Function to find the length of the longest consecutive sequence in a binary tree.
16 int longestConsecutive(TreeNode* root) {
17 int maxLength = 0; // Initialize the maxLength to keep track of the longest sequence.
18
19 // Recursive lambda function to perform a depth-first search on the tree.
20 // It returns the length of the longest consecutive sequence starting at 'node'.
21 std::function<int(TreeNode*)> dfs = [&](TreeNode* node) {
22 if (!node) {
23 // If the node is null, return 0 since there is no sequence.
24 return 0;
25 }
26 // Recursively find the length of the longest consecutives sequences in the left and right subtrees.
27 // We start by assuming both sequences include the current node, hence +1.
28 int leftLength = dfs(node->left) + 1;
29 int rightLength = dfs(node->right) + 1;
30
31 // If the left child exists and doesn't follow the consecutive property, reset the leftLength.
32 if (node->left && node->left->val - node->val != 1) {
33 leftLength = 1;
34 }
35
36 // If the right child exists and doesn't follow the consecutive property, reset the rightLength.
37 if (node->right && node->right->val - node->val != 1) {
38 rightLength = 1;
39 }
40
41 // The longest sequence at the current node is the max of the left and right sequences.
42 int currentLength = std::max(leftLength, rightLength);
43
44 // Update the maxLength if the sequence at the current node is the longest one found so far.
45 maxLength = std::max(maxLength, currentLength);
46
47 // Return the length of the longest sequence at the current node.
48 return currentLength;
49 };
50
51 // Perform the depth-first search starting from the root.
52 dfs(root);
53
54 // After the DFS, maxLength contains the length of the longest consecutive sequence in the tree.
55 return maxLength;
56 }
57};
58
1// Type definition for a binary tree node.
2interface TreeNode {
3 val: number;
4 left: TreeNode | null;
5 right: TreeNode | null;
6}
7
8// Function that returns the length of the longest consecutive sequence in the binary tree.
9function longestConsecutive(root: TreeNode | null): number {
10 let longestSequence = 0; // This variable will hold the longest sequence found.
11
12 // Helper function that performs depth-first search to find the longest consecutive sequence.
13 const findLongestConsecutive = (currentNode: TreeNode | null): number => {
14 if (currentNode === null) {
15 return 0;
16 }
17
18 // Recursively find the lengths for left and right subtrees.
19 let leftLength = findLongestConsecutive(currentNode.left) + 1;
20 let rightLength = findLongestConsecutive(currentNode.right) + 1;
21
22 // If the left child is not consecutively increasing, reset the left sequence length.
23 if (currentNode.left && currentNode.left.val - currentNode.val !== 1) {
24 leftLength = 1;
25 }
26
27 // If the right child is not consecutively increasing, reset the right sequence length.
28 if (currentNode.right && currentNode.right.val - currentNode.val !== 1) {
29 rightLength = 1;
30 }
31
32 // Take the longer sequence of the two.
33 const currentMax = Math.max(leftLength, rightLength);
34
35 // Update the longest sequence found so far.
36 longestSequence = Math.max(longestSequence, currentMax);
37
38 // Return the longer sequence found at the current node.
39 return currentMax;
40 };
41
42 // Start the search from the root of the binary tree.
43 findLongestConsecutive(root);
44
45 // Return the longest consecutive sequence length found.
46 return longestSequence;
47}
48
Time and Space Complexity
The given code performs a depth-first search (DFS) on a binary tree to find the length of the longest consecutive sequence.
Time Complexity:
The time complexity of the DFS function is O(N)
, where N
is the total number of nodes in the binary tree. This is because the algorithm visits each node exactly once to calculate the longest consecutive path that includes that node as the sequence start.
Space Complexity:
The space complexity of the DFS function is O(H)
, where H
is the height of the binary tree. The reason for this is the recursive stack space which, in the worst-case scenario (a completely unbalanced tree), could be equal to the number of nodes N
(thus O(N)
), but for a balanced tree, it will be O(log N)
. H
captures this as it ranges from log N
to N
depending on the balance of the tree.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https algomonster s3 us east 2 amazonaws com cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS
Binary Tree Min Depth Prereq BFS on Tree problems bfs_intro Given a binary tree find the depth of the shallowest leaf node https algomonster s3 us east 2 amazonaws com binary_tree_min_depth png Explanation We can solve this problem with either DFS or BFS With DFS we traverse the whole tree looking for leaf nodes and record and update the minimum depth as we go With BFS though since we search level by level we are guaranteed to find the shallowest leaf node
Want a Structured Path to Master System Design Too? Don’t Miss This!