You are given the root of a binary tree and the head of a linked list. Determine if there exists a downward path in the binary tree that matches the values of the linked list nodes in sequence.
A downward path is defined as a path starting from any node in the tree and moving downwards to its children (either left or right child).
For example:
Binary Tree: Linked List:
1 4 -> 2 -> 8
/ \
4 4
/ \ / \
null 2 2 null
/ \ / \
1 null 6 8
/ \
1 3
In this case, the function should return true
because the linked list 4 -> 2 -> 8
exists as a downward path in the tree starting from the node with value 4 (the left child of the root).
Here are a few more examples:
head = [1, 4, 2, 6], root = [1, 4, 4, null, 2, 2, null, 1, null, 6, 8, null, null, null, null, 1, 3]
should return true
head = [1, 4, 2, 6, 8], root = [1, 4, 4, null, 2, 2, null, 1, null, 6, 8, null, null, null, null, 1, 3]
should return false
Write an algorithm to efficiently solve this problem. Consider edge cases such as empty trees, empty lists, and mismatches in node values.
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. |