You are given a binary tree root
and a linked list with head
as the first node. Determine whether all the elements in the linked list, starting from the head
, correspond to some downward path connected in the binary tree. A downward path means a path that starts at some node and goes downwards (towards the leaves).
Examples:
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 4 -> 2 -> 8
in the binary tree form a subpath matching the linked list.
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
Explanation:
Nodes 1 -> 4 -> 2 -> 6
in the binary tree form a subpath matching the linked list.
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
.
Clarifications:
true
if any downward path in the binary tree matches the linked list.How would you approach this problem? Consider edge cases such as empty trees or lists, and aim for an efficient solution.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method exhaustively explores all potential placements of the linked list within the binary tree. Think of trying to fit a specific jigsaw piece into every possible location of a larger puzzle. If a potential placement matches the linked list's sequence, we have a match.
Here's how the algorithm would work step-by-step:
def is_linked_list_in_binary_tree(binary_tree_root, linked_list_head):
def check_from_node(tree_node, list_node):
if not list_node:
return True
if not tree_node:
return False
if tree_node.val != list_node.val:
return False
return check_from_node(tree_node.left, list_node.next) or \
check_from_node(tree_node.right, list_node.next)
def traverse_tree(tree_node, linked_list_head):
if not linked_list_head:
return True
if not tree_node:
return False
# Check if linked list matches starting from this node
if check_from_node(tree_node, linked_list_head):
return True
# Recursively check the left and right subtrees
return traverse_tree(tree_node.left, linked_list_head) or \
traverse_tree(tree_node.right, linked_list_head)
# Need to handle empty linked list case
if not linked_list_head:
return True
# Initiate the traversal from the root of the binary tree
return traverse_tree(binary_tree_root, linked_list_head)
The best way to solve this is to first check if the tree contains the first value of the linked list. If it does, we need to check if the linked list exists starting from that node in the tree. We do this recursively by walking through the linked list and the tree simultaneously.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, value=0, next=None):
self.value = value
self.next = next
class TreeNode:
def __init__(self, value=0, left=None, right=None):
self.value = value
self.left = left
self.right = right
def is_sub_path(linked_list_head, root_node):
if not linked_list_head:
return True
if not root_node:
return False
# Check if the linked list exists starting from each node.
def check_sub_path_from_node(tree_node, list_node):
if not list_node:
return True
if not tree_node:
return False
# If the values match, recursively check the subtrees.
if tree_node.value == list_node.value:
return check_sub_path_from_node(tree_node.left, list_node.next) or \
check_sub_path_from_node(tree_node.right, list_node.next)
else:
return False
# Main part, finds a node that matches linked list head.
def find_list_head_in_tree(tree_node, list_head):
if not tree_node:
return False
# Check if the current node matches the list head or continue searching.
if tree_node.value == list_head.value:
if check_sub_path_from_node(tree_node, list_head):
return True
return find_list_head_in_tree(tree_node.left, list_head) or \
find_list_head_in_tree(tree_node.right, list_head)
# First, check if head exists in the tree
return find_list_head_in_tree(root_node, linked_list_head)
Case | How to Handle |
---|---|
Empty linked list | Return true immediately as an empty list is considered a subtree of any binary tree. |
Empty binary tree | Return false immediately if the binary tree is empty because a linked list cannot be a subtree of an empty tree. |
Linked list longer than any path in the binary tree | The algorithm will eventually reach a leaf node and return false if the linked list isn't found. |
Linked list and binary tree both have a single node | Check if the values of the single nodes are equal and return true if they are; otherwise, return false. |
Binary tree with skewed distribution (e.g., all nodes on one side) | The algorithm will traverse the skewed side and either find the list or exhaust the path returning false. |
Multiple occurrences of the linked list sequence within the binary tree | The algorithm should return true as soon as the linked list is found once. |
Large binary tree and linked list leading to potential stack overflow with recursion | Consider iterative approach with explicit stack or queue to prevent stack overflow in deep trees. |
Linked list values are identical to values on a non-matching path | The solution must ensure the full linked list sequence is found, not just matching individual values on different paths. |