
Problem Statement
The task involves determining if a linked list can be represented as a downward path in a given binary tree. A downward path is defined as a sequence that begins at any node in the binary tree and progresses downwards. For the linked list to correspond to a downward path within the binary tree, each element, starting from the head of the list, must sequentially match the values of the nodes along this path in the tree.
Examples
Example 1
Input:
head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
Output:
true
Explanation:
Nodes in blue form a subpath in the binary Tree.
Example 2
Input:
head = [1,4,2,6], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
Output:
true
Example 3
Input:
head = [1,4,2,6,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
Output:
false
Explanation:
There is no path in the binary tree that contains all the elements of the linked list from `head`.
Constraints
- The number of nodes in the tree will be in the range
[1, 2500]
. - The number of nodes in the list will be in the range
[1, 100]
. 1 <= Node.val <= 100
for each node in the linked list and binary tree.
Approach and Intuition
To tackle this problem, a systematic approach needs to be followed by considering how the linked list could fit into the binary tree's structure:
- Begin at the root node of the binary tree and try to find a node that has the same value as the head of the linked list.
- From this starting node, traverse through the binary tree by following the linked list. For each node in the binary tree:
- Compare the current binary tree node's value with the current linked list node's value.
- If the values match, move to the next node in both the linked list and the binary tree (children of the current binary tree node).
- If at any point values do not match or the linked list is not exhausted after reaching a leaf node in the tree, backtrack and explore a different path from the same starting node.
- If no starting node in the binary tree matches the head of the linked list, or if a complete match for the entire list is not found in any downward path from these starting positions, return false. Otherwise, if a complete path correspondence is found, return true.
This problem effectively reduces to recursively scanning each potential starting node in the binary tree and attempting to map each subsequent node in the linked list to a downward path originating from this candidate node. This solution requires both backtracking for exploring different potential starts and deep traversal to ensure the entire linked list can be mapped to nodes in the tree over potential paths.
Solutions
- C++
- Java
- Python
class Solution {
public:
bool isSubsequence(ListNode* listHead, TreeNode* treeRoot) {
// Preprocess the linked list to create search pattern
vector<int> nodeValues = {listHead->val}, jumpTable = {0};
int nextMatch = 0;
listHead = listHead->next;
while (listHead) {
while (nextMatch && listHead->val != nodeValues[nextMatch])
nextMatch = jumpTable[nextMatch - 1];
nextMatch += listHead->val == nodeValues[nextMatch];
nodeValues.push_back(listHead->val);
jumpTable.push_back(nextMatch);
listHead = listHead->next;
}
// Use Depth First Search to find the sequence in the binary tree
return findSequence(treeRoot, 0, nodeValues, jumpTable);
}
private:
bool findSequence(TreeNode* treeNode, int currentValueIndex,
const vector<int>& nodeValues,
const vector<int>& jumpTable) {
if (!treeNode) return false;
while (currentValueIndex && treeNode->val != nodeValues[currentValueIndex])
currentValueIndex = jumpTable[currentValueIndex - 1];
currentValueIndex += treeNode->val == nodeValues[currentValueIndex];
// Confirm if entire sequence is found
if (currentValueIndex == nodeValues.size()) return true;
// Continue search on left and right children
return findSequence(treeNode->left, currentValueIndex, nodeValues, jumpTable) ||
findSequence(treeNode->right, currentValueIndex, nodeValues, jumpTable);
}
};
This C++ solution implements a function to determine if a linked list is a subsequence of the paths in a binary tree. Here’s a breakdown of how the solution is structured and operates:
- A primary function,
isSubsequence
, preprocesses the linked list by extracting its values into a vector and creating a jump table utilizing the KMP (Knuth-Morris-Pratt) pattern matching algorithm's partial match table. This table helps in efficiently finding subsequences. - It then performs a depth-first search (DFS) on the binary tree to locate a path that matches the linked list sequence using the preprocessed vector and jump table.
- The secondary function,
findSequence
, conducts the DFS. It compares each node's value against the expected sequence from the linked list. If a mismatch occurs, the function utilizes the jump table to skip unnecessary comparisons, speeding up the search process. - The DFS continues recursively through both left and right children of the current node until it either finds the complete sequence, indicating the linked list is a subsequence of a path in the tree, or exhausts all paths without a match.
This method ensures that the algorithm efficiently processes and matches the linked list against all paths in the binary tree, handling the problem with both effectiveness and optimal performance in mind.
class Solution {
public boolean checkSubPath(ListNode head, TreeNode root) {
List<Integer> sequence = new ArrayList<>();
List<Integer> lps = new ArrayList<>();
sequence.add(head.val);
lps.add(0);
int idx = 0;
head = head.next;
while (head != null) {
while (idx > 0 && head.val != sequence.get(idx)) {
idx = lps.get(idx - 1);
}
idx += head.val == sequence.get(idx) ? 1 : 0;
sequence.add(head.val);
lps.add(idx);
head = head.next;
}
return dfsCheck(root, 0, sequence, lps);
}
private boolean dfsCheck(
TreeNode node,
int idx,
List<Integer> sequence,
List<Integer> lps
) {
if (node == null) return false;
while (idx > 0 && node.val != sequence.get(idx)) {
idx = lps.get(idx - 1);
}
idx += node.val == sequence.get(idx) ? 1 : 0;
if (idx == sequence.size()) return true;
return dfsCheck(node.left, idx, sequence, lps) || dfsCheck(node.right, idx, sequence, lps);
}
}
The solution provided consists of a Java class named Solution
that checks if a linked list is a subpath within a binary tree. The class implements a method checkSubPath
which accepts the head of the linked list and the root of the binary tree as parameters.
- The
checkSubPath
method first constructs a list of integers (sequence
) from the linked list and calculates the Longest Prefix Suffix (LPS) array (lps
) for KMP algorithm application. - For each node in the binary tree, the method then recursively checks (using
dfsCheck
) if there exists a path starting from this node that matches the sequence derived from the linked list using depth-first search approach.
Key components of the implementation:
Sequence and LPS Array Construction:
- Converts the linked list into an array of values (
sequence
). - Builds an array (
lps
) that represents the longest prefix which is also a suffix for each prefix of the array.
- Converts the linked list into an array of values (
Depth-First Search (DFS) Check:
- The
dfsCheck
private method recursively explores each node of the binary tree to match the sequence using the LPS array for efficient mismatch handling. - It simultaneously traverses the binary tree and checks sequence match, adjusting the position based on LPS in case of mismatches.
- The
Base and Recursive Cases:
- The base case for recursion is when the current tree node is
null
, returningfalse
. - On a match, the search continues left and right, deepening into the tree structure.
- If the entire sequence matches at any point,
true
is returned, indicating a complete match was found.
- The base case for recursion is when the current tree node is
Performance Consideration: The use of KMP pattern matching principle via the LPS array helps in optimizing the solution by reducing unnecessary comparisons, especially when backtracking on mismatches.
This implementation not only ensures the matching of the linked list sequence within the binary tree paths but does so efficiently by adopting a systematic combination of linear sequence transformation and deep tree traversal strategies.
class Solution:
def hasSubPath(
self, list_head: Optional[ListNode], tree_root: Optional[TreeNode]
) -> bool:
# Building the sequence from the linked list values
sequence = [list_head.val]
lps = [0]
seq_idx = 0
list_head = list_head.next
while list_head:
while seq_idx > 0 and list_head.val != sequence[seq_idx]:
seq_idx = lps[seq_idx - 1]
if list_head.val == sequence[seq_idx]:
seq_idx += 1
sequence.append(list_head.val)
lps.append(seq_idx)
list_head = list_head.next
# Start DFS to find the sequence in binary tree
return self._dfs_search(tree_root, 0, sequence, lps)
def _dfs_search(
self,
tree_node: Optional[TreeNode],
seq_idx: int,
sequence: List[int],
lps: List[int],
) -> bool:
if not tree_node:
return False
while seq_idx > 0 and tree_node.val != sequence[seq_idx]:
seq_idx = lps[seq_idx - 1]
if tree_node.val == sequence[seq_idx]:
seq_idx += 1
# If entire sequence is found
if seq_idx == len(sequence):
return True
# Recurse left and right
return self._dfs_search(
tree_node.left, seq_idx, sequence, lps
) or self._dfs_search(
tree_node.right, seq_idx, sequence, lps
)
This Python solution tackles the problem of determining whether a linked list exists as a subpath within a binary tree. The approach combines traversal and pattern matching techniques, specifically Depth-First Search (DFS) and the Knuth-Morris-Pratt (KMP) algorithm for sequence matching.
The solution involves two primary components:
Sequence Construction from Linked List:
- Start by extracting the values from the linked list into a sequence list.
- Use the KMP algorithm's partial match table to optimize the search process, storing the lengths of the longest proper prefix which is also a suffix in the
lps
list. - Iterate through the linked list, updating the sequence and
lps
list based on the KMP algorithm's criteria.
DFS to Find the Sequence in Binary Tree:
- Implement a recursive
_dfs_search
function to explore the binary tree. - For each node, the function checks if the node's value matches the current index of the sequence using the partial match table from the KMP algorithm to handle mismatches efficiently.
- If the entire sequence matches at any point during the DFS traversal of the tree, it returns
True
. - If no match is found by the end of the tree traversal, it returns
False
.
- Implement a recursive
The solution effectively uses the combination of sequence construction from the linked list and subsequent DFS to locate this sequence within the binary tree. This method ensures optimal performance by leveraging the preprocessing step of the KMP algorithm in the sequence matching process within the DFS traversal, minimizing unnecessary comparisons.
No comments yet.