|
1 | 1 | # [1367. Linked List in Binary Tree](https://leetcode.com/problems/linked-list-in-binary-tree/description/) |
2 | 2 |
|
3 | | -## Clarification Questions |
4 | | -* No, it's clear from problem description. |
5 | | - |
6 | 3 | ## Test Cases |
7 | | -### Normal Cases |
8 | | -``` |
9 | | -Input: |
10 | | -Output: |
11 | | -``` |
12 | 4 | ### Edge / Corner Cases |
13 | 5 | * The linked list or tree is empty. |
14 | 6 | * The linked list is longer than the tree. |
| 7 | +``` |
15 | 8 | list: 1 -> 2 -> 3 |
16 | 9 | tree: |
17 | | -``` |
18 | 10 | 1 |
19 | 11 | / \ |
20 | 12 | 2 3 |
21 | 13 | ``` |
22 | 14 |
|
23 | 15 | * The linked list is shorter than the tree. |
| 16 | +``` |
24 | 17 | list: 1 |
25 | 18 | tree: |
26 | | -``` |
27 | 19 | 1 |
28 | 20 | / \ |
29 | 21 | 2 3 |
30 | 22 | ``` |
31 | 23 |
|
| 24 | +* Tree is the subsequence of the linked list. |
| 25 | +``` |
| 26 | +list: 5 -> 8 |
| 27 | +tree: |
| 28 | + 5 |
| 29 | + / |
| 30 | + 2 |
| 31 | + / |
| 32 | +8 |
| 33 | +``` |
| 34 | + |
32 | 35 | # Approach |
33 | | -It's similar to [572. Subtree of Another Tree](https://leetcode.com/problems/subtree-of-another-tree/), but we need to check if the linked list is a "substring" of the tree, not a subtree. (All the node in linked list have to be in the tree, but not all tree nodes have to be in the linked list.) |
| 36 | +It's similar to [572. Subtree of Another Tree](../leetcode/572.subtree-of-another-tree.md), but we need to check if the linked list is a "substring" of the tree, not a subtree. (All the node in linked list have to be in the tree, but not all tree nodes have to be in the linked list.) |
34 | 37 |
|
35 | 38 | ```kotlin |
36 | 39 | fun isSubPath(head: ListNode?, root: TreeNode?): Boolean { |
37 | | - // Slight different between `contains()`, we have to decide base on the definition of this recursive function. |
38 | | - if (head == null && root == null) return true // Both are empty, is a subpath. |
39 | | - if (head == null || root == null) return false // One of them is empty, is not a subpath. |
40 | | - |
| 40 | + if (root == null) return false |
| 41 | + |
41 | 42 | // Search the linked list starting from the root node. |
42 | 43 | return contains(head, root) || |
43 | 44 | isSubPath(head, root.left) || isSubPath(head, root.right) // Or search the linked list in the left and right subtree. |
44 | 45 | } |
45 | 46 |
|
46 | 47 | private fun contains(head: ListNode?, root: TreeNode?): Boolean { |
| 48 | + if (head == null && root == null) return true |
47 | 49 | // Tree contains empty list or we reach the end of the list, then it's a subpath. |
48 | | - if (head == null) return true |
| 50 | + if (head == null && root != null) return true |
49 | 51 | // It means we have reached the end of the tree, but the linked list is not found. |
50 | | - if (root == null) return false |
| 52 | + if (head != null && root == null) return false |
51 | 53 |
|
52 | 54 | // Keep searching the linked list in the tree. |
53 | 55 | return head.`val` == root.`val` && (contains(head.next, root.left) || contains(head.next, root.right)) |
54 | 56 | } |
55 | 57 | ``` |
56 | 58 |
|
57 | 59 | * **Time Complexity:** `O(N * M)`, where `N` is the number of nodes in the tree and `M` is the number of nodes in the linked list. |
58 | | -* **Space Complexity:** `O(N + M)`, we have to travesal the tree `O(N)` and we check `contain()` which takes `O(M)` space in each tree node. |
| 60 | +* **Space Complexity:** `O(N + M)`, we have to travesal the tree `O(N)` and we check `contain()` which takes `O(M)` space in each tree node. |
| 61 | + |
| 62 | + |
| 63 | +## WA |
| 64 | +```js |
| 65 | +head = 1 -> 8 |
| 66 | +root = |
| 67 | + 1 |
| 68 | + / |
| 69 | + 6 |
| 70 | + / |
| 71 | + 8 |
| 72 | +``` |
| 73 | +```kotlin |
| 74 | +fun isSubPath(head: ListNode?, root: TreeNode?): Boolean { |
| 75 | + if (head == null && root == null) return true |
| 76 | + if (head == null && root != null) return true |
| 77 | + if (head != null && root == null) return false |
| 78 | + |
| 79 | + val h = head!! |
| 80 | + val r = root!! |
| 81 | + val result1 = |
| 82 | + h.`val` == r.`val` && ( |
| 83 | + isSubPath(h.next, r.left) || |
| 84 | + isSubPath(h.next, r.right)) |
| 85 | + |
| 86 | + val result2 = |
| 87 | + isSubPath(h, r.left) || |
| 88 | + isSubPath(h, r.right) |
| 89 | + |
| 90 | + return result1 || result2 |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +* `result1` represents the case that the linked list starts from the current node in the tree. And keep searching if all the nodes in the linked list are in the tree. It's **a continuation of the previous search**. |
| 95 | +* `result2` represents the case that we search the beginning of the linked list in the left and right subtree of the current node. It's **a new search**, not a continuation of the previous search. |
| 96 | + |
| 97 | +The problem here is that `isSubPath()` will check current `root` and **also start a new search** in the left and right subtree. But we should not start a new search in `result1`, it should be a continuation of the previous search. We should create a new function to handle the continuation of the previous search. |
| 98 | + |
| 99 | +Corrected version: |
| 100 | +```kotlin |
| 101 | +fun isSubPath(head: ListNode?, root: TreeNode?): Boolean { |
| 102 | + if (head == null && root == null) return true |
| 103 | + if (head == null && root != null) return true |
| 104 | + if (head != null && root == null) return false |
| 105 | + |
| 106 | + val h = head!! |
| 107 | + val r = root!! |
| 108 | + // Check the current and continue the search from the previous search. |
| 109 | + val result1 = |
| 110 | + h.`val` == r.`val` && ( |
| 111 | + search(h.next, r.left) || // Modified |
| 112 | + search(h.next, r.right)) // Modified |
| 113 | + |
| 114 | + // Start a new search in the left and right subtree. |
| 115 | + val result2 = |
| 116 | + isSubPath(h, r.left) || |
| 117 | + isSubPath(h, r.right) |
| 118 | + |
| 119 | + return result1 || result2 |
| 120 | +} |
| 121 | + |
| 122 | +// It's a continuation of the previous search. |
| 123 | +private fun search(head: ListNode?, root: TreeNode?): Boolean { |
| 124 | + // Same as above. |
| 125 | + if (head == null && root == null) return true |
| 126 | + if (head == null && root != null) return true |
| 127 | + if (head != null && root == null) return false |
| 128 | + |
| 129 | + val h = head!! |
| 130 | + val r = root!! |
| 131 | + return h.`val` == r.`val` && (search(head.next, root.left) || search(head.next, root.right)) |
| 132 | +} |
| 133 | +``` |
0 commit comments