# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# 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 isSubPath(self, head: ListNode, root: TreeNode) -> bool:
if not root:
return False
def dfs(tree, list_head):
if not list_head:
return True
if not tree:
return False
if tree.val != list_head.val:
return False
return dfs(tree.left, list_head.next) or dfs(tree.right, list_head.next)
return (dfs(root, head) or
self.isSubPath(head, root.left) or
self.isSubPath(head, root.right))
// Definition for singly-linked list.
// struct ListNode {
// int val;
// ListNode *next;
// ListNode() : val(0), next(nullptr) {}
// ListNode(int x) : val(x), next(nullptr) {}
// ListNode(int x, ListNode *next) : val(x), next(next) {}
// };
// Definition for a binary tree node.
// struct TreeNode {
// int val;
// TreeNode *left;
// TreeNode *right;
// TreeNode() : val(0), left(nullptr), right(nullptr) {}
// TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
// TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
// };
class Solution {
public:
bool isSubPath(ListNode* head, TreeNode* root) {
if (!root) return false;
return dfs(root, head) || isSubPath(head, root->left) || isSubPath(head, root->right);
}
bool dfs(TreeNode* node, ListNode* head) {
if (!head) return true;
if (!node) return false;
if (node->val != head->val) return false;
return dfs(node->left, head->next) || dfs(node->right, head->next);
}
};
// Definition for singly-linked list.
// public class ListNode {
// int val;
// ListNode next;
// ListNode() {}
// ListNode(int val) { this.val = val; }
// ListNode(int val, ListNode next) { this.val = val; this.next = next; }
// }
// Definition for a binary tree node.
// public class TreeNode {
// int val;
// TreeNode left;
// TreeNode right;
// TreeNode() {}
// TreeNode(int val) { this.val = val; }
// TreeNode(int val, TreeNode left, TreeNode right) {
// this.val = val;
// this.left = left;
// this.right = right;
// }
// }
class Solution {
public boolean isSubPath(ListNode head, TreeNode root) {
if (root == null) return false;
return dfs(root, head) || isSubPath(head, root.left) || isSubPath(head, root.right);
}
private boolean dfs(TreeNode node, ListNode head) {
if (head == null) return true;
if (node == null) return false;
if (node.val != head.val) return false;
return dfs(node.left, head.next) || dfs(node.right, head.next);
}
}
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* 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 {ListNode} head
* @param {TreeNode} root
* @return {boolean}
*/
var isSubPath = function(head, root) {
if (!root) return false;
function dfs(node, listNode) {
if (!listNode) return true;
if (!node) return false;
if (node.val !== listNode.val) return false;
return dfs(node.left, listNode.next) || dfs(node.right, listNode.next);
}
return dfs(root, head) || isSubPath(head, root.left) || isSubPath(head, root.right);
};
You are given the head of a singly linked list head
and the root node of a binary tree root
. Your task is to determine whether there exists a downward path in the binary tree (starting from any node and moving only to its children) such that the sequence of values along this path is the same as the sequence of values in the linked list.
true
if such a path exists, false
otherwise.At first glance, the problem seems to require checking every possible path in the binary tree and comparing it to the linked list. The straightforward way is to try to match the linked list starting from every node in the tree. For each tree node, we attempt to "walk" down the tree and the list at the same time, matching their values. If at any point the values do not match, or the tree path ends before the list, that path fails.
However, this brute-force approach can be inefficient if the tree and list are large. We may end up repeating the same checks multiple times. To optimize, we can use recursion to check for matches and terminate early as soon as a mismatch is found. Since the binary tree can branch in two directions, we need to check both left and right children at each step.
The key insight is that for each node in the tree, we need to check two things:
The solution uses a recursive, depth-first search (DFS) strategy to compare the linked list with every possible downward path in the binary tree. Here's how it works:
dfs
) that takes a tree node and a list node as arguments. This function checks if the path starting from the current tree node matches the remaining linked list.
null
, it means we've matched the entire list, so return true
.null
but the list isn't finished, return false
.false
.true
, the path is found.dfs
function with the head of the linked list. If a match isn't found, recursively check the left and right subtrees with the original head of the list.true
; otherwise, return false
.
This approach ensures we efficiently check every possible starting point and path in the binary tree without unnecessary recomputation.
Let's walk through a concrete example:
Suppose the linked list is 1 -> 4 -> 2
, and the binary tree is:
1 / \ 4 4 / / 2 2 / / 1 6
null
, so we have matched the entire list. Return true
.
The function would return true
because a downward path matching the linked list exists in the tree.
Brute-force approach:
In summary, the "Linked List in Binary Tree" problem is solved by recursively checking every possible downward path in the binary tree to see if it matches the given linked list. The approach is elegant because it combines a depth-first search with careful handling of base cases, allowing for early termination and efficient traversal. By leveraging recursion, we ensure that all possible starting points are considered, and the solution remains concise and clear.