|
| 1 | +# 题目地址(1261. 在受污染的二叉树中查找元素) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/find-elements-in-a-contaminated-binary-tree/submissions/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +给出一个满足下述规则的二叉树: |
| 9 | +
|
| 10 | +root.val == 0 |
| 11 | +如果 treeNode.val == x 且 treeNode.left != null,那么 treeNode.left.val == 2 * x + 1 |
| 12 | +如果 treeNode.val == x 且 treeNode.right != null,那么 treeNode.right.val == 2 * x + 2 |
| 13 | +现在这个二叉树受到「污染」,所有的 treeNode.val 都变成了 -1。 |
| 14 | +
|
| 15 | +请你先还原二叉树,然后实现 FindElements 类: |
| 16 | +
|
| 17 | +FindElements(TreeNode* root) 用受污染的二叉树初始化对象,你需要先把它还原。 |
| 18 | +bool find(int target) 判断目标值 target 是否存在于还原后的二叉树中并返回结果。 |
| 19 | + |
| 20 | +
|
| 21 | +示例 1: |
| 22 | +
|
| 23 | + |
| 24 | +
|
| 25 | +输入: |
| 26 | +["FindElements","find","find"] |
| 27 | +[[[-1,null,-1]],[1],[2]] |
| 28 | +输出: |
| 29 | +[null,false,true] |
| 30 | +解释: |
| 31 | +FindElements findElements = new FindElements([-1,null,-1]); |
| 32 | +findElements.find(1); // return False |
| 33 | +findElements.find(2); // return True |
| 34 | +示例 2: |
| 35 | +
|
| 36 | + |
| 37 | +
|
| 38 | +输入: |
| 39 | +["FindElements","find","find","find"] |
| 40 | +[[[-1,-1,-1,-1,-1]],[1],[3],[5]] |
| 41 | +输出: |
| 42 | +[null,true,true,false] |
| 43 | +解释: |
| 44 | +FindElements findElements = new FindElements([-1,-1,-1,-1,-1]); |
| 45 | +findElements.find(1); // return True |
| 46 | +findElements.find(3); // return True |
| 47 | +findElements.find(5); // return False |
| 48 | +示例 3: |
| 49 | +
|
| 50 | + |
| 51 | +
|
| 52 | +输入: |
| 53 | +["FindElements","find","find","find","find"] |
| 54 | +[[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]] |
| 55 | +输出: |
| 56 | +[null,true,false,false,true] |
| 57 | +解释: |
| 58 | +FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]); |
| 59 | +findElements.find(2); // return True |
| 60 | +findElements.find(3); // return False |
| 61 | +findElements.find(4); // return False |
| 62 | +findElements.find(5); // return True |
| 63 | + |
| 64 | +
|
| 65 | +提示: |
| 66 | +
|
| 67 | +TreeNode.val == -1 |
| 68 | +二叉树的高度不超过 20 |
| 69 | +节点的总数在 [1, 10^4] 之间 |
| 70 | +调用 find() 的总次数在 [1, 10^4] 之间 |
| 71 | +0 <= target <= 10^6 |
| 72 | +
|
| 73 | +``` |
| 74 | + |
| 75 | +## 暴力法 |
| 76 | + |
| 77 | +### 思路 |
| 78 | + |
| 79 | +最简单想法就是递归建立树,然后 find 的时候递归查找即可,代码也很简单。 |
| 80 | + |
| 81 | +### 代码 |
| 82 | + |
| 83 | +Pythpn Code: |
| 84 | + |
| 85 | +```python |
| 86 | +# Definition for a binary tree node. |
| 87 | +# class TreeNode: |
| 88 | +# def __init__(self, x): |
| 89 | +# self.val = x |
| 90 | +# self.left = None |
| 91 | +# self.right = None |
| 92 | + |
| 93 | +class FindElements: |
| 94 | + node = None |
| 95 | + def __init__(self, root: TreeNode): |
| 96 | + def recover(node): |
| 97 | + if not node: |
| 98 | + return node; |
| 99 | + if node.left: |
| 100 | + node.left.val = 2 * node.val + 1 |
| 101 | + if node.right: |
| 102 | + node.right.val = 2 * node.val + 2 |
| 103 | + recover(node.left) |
| 104 | + recover(node.right) |
| 105 | + return node |
| 106 | + root.val = 0 |
| 107 | + self.node = recover(root) |
| 108 | + |
| 109 | + |
| 110 | + def find(self, target: int) -> bool: |
| 111 | + def findInTree(node, target): |
| 112 | + if not node: |
| 113 | + return False |
| 114 | + if node.val == target: |
| 115 | + return True |
| 116 | + return findInTree(node.left, target) or findInTree(node.right, target) |
| 117 | + return findInTree(self.node, target) |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | + |
| 122 | +# Your FindElements object will be instantiated and called as such: |
| 123 | +# obj = FindElements(root) |
| 124 | +# param_1 = obj.find(target) |
| 125 | +``` |
| 126 | + |
| 127 | +上述代码会超时,我们来考虑优化。 |
| 128 | + |
| 129 | +## 空间换时间 |
| 130 | + |
| 131 | +### 思路 |
| 132 | + |
| 133 | +上述代码会超时,我们考虑使用空间换时间。 建立树的时候,我们将所有值存到一个集合中去。当需要 find 的时候,我们直接查找 set 即可,时间复杂度 O(1)。 |
| 134 | + |
| 135 | +### 代码 |
| 136 | + |
| 137 | +```python |
| 138 | +# Definition for a binary tree node. |
| 139 | +# class TreeNode: |
| 140 | +# def __init__(self, x): |
| 141 | +# self.val = x |
| 142 | +# self.left = None |
| 143 | +# self.right = None |
| 144 | + |
| 145 | +class FindElements: |
| 146 | + def __init__(self, root: TreeNode): |
| 147 | + # set 不能放在init外侧。 因为测试用例之间不会销毁FindElements的变量 |
| 148 | + self.seen = set() |
| 149 | + def recover(node): |
| 150 | + if not node: |
| 151 | + return node; |
| 152 | + if node.left: |
| 153 | + node.left.val = 2 * node.val + 1 |
| 154 | + self.seen.add(node.left.val) |
| 155 | + if node.right: |
| 156 | + node.right.val = 2 * node.val + 2 |
| 157 | + self.seen.add(node.right.val) |
| 158 | + recover(node.left) |
| 159 | + recover(node.right) |
| 160 | + return node |
| 161 | + root.val = 0 |
| 162 | + self.seen.add(0) |
| 163 | + self.node = recover(root) |
| 164 | + |
| 165 | + |
| 166 | + def find(self, target: int) -> bool: |
| 167 | + return target in self.seen |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | +# Your FindElements object will be instantiated and called as such: |
| 173 | +# obj = FindElements(root) |
| 174 | +# param_1 = obj.find(target) |
| 175 | +``` |
| 176 | + |
| 177 | +这种解法可以 AC,但是在数据量非常大的时候,可能 MLE,我们继续考虑优化。 |
| 178 | + |
| 179 | +## 二进制法 |
| 180 | + |
| 181 | +### 思路 |
| 182 | + |
| 183 | +这是一种非常巧妙的做法。 |
| 184 | + |
| 185 | +如果我们把树中的数全部加 1 会怎么样? |
| 186 | + |
| 187 | + |
| 188 | +(图参考 https://leetcode.com/problems/find-elements-in-a-contaminated-binary-tree/discuss/431229/Python-Special-Way-for-find()-without-HashSet-O(1)-Space-O(logn)-Time) |
| 189 | + |
| 190 | +仔细观察发现,每一行的左右子树分别有不同的前缀: |
| 191 | + |
| 192 | + |
| 193 | + |
| 194 | +Ok,那么算法就来了。为了便于理解,我们来举个具体的例子,比如 target 是 9,我们首先将其加 1,二进制表示就是 1010。不考虑第一位,就是 010,我们只要: |
| 195 | + |
| 196 | +- 0 向左 👈 |
| 197 | +- 1 向右 👉 |
| 198 | +- - 0 向左 👈 |
| 199 | + |
| 200 | +就可以找到 9 了。 |
| 201 | + |
| 202 | +> 0 表示向左 , 1 表示向右 |
| 203 | +
|
| 204 | +### 代码 |
| 205 | + |
| 206 | +```python |
| 207 | +# Definition for a binary tree node. |
| 208 | +# class TreeNode: |
| 209 | +# def __init__(self, x): |
| 210 | +# self.val = x |
| 211 | +# self.left = None |
| 212 | +# self.right = None |
| 213 | + |
| 214 | +class FindElements: |
| 215 | + node = None |
| 216 | + def __init__(self, root: TreeNode): |
| 217 | + def recover(node): |
| 218 | + if not node: |
| 219 | + return node; |
| 220 | + if node.left: |
| 221 | + node.left.val = 2 * node.val + 1 |
| 222 | + if node.right: |
| 223 | + node.right.val = 2 * node.val + 2 |
| 224 | + recover(node.left) |
| 225 | + recover(node.right) |
| 226 | + return node |
| 227 | + root.val = 0 |
| 228 | + self.node = recover(root) |
| 229 | + |
| 230 | + |
| 231 | + def find(self, target: int) -> bool: |
| 232 | + node = self.node |
| 233 | + for bit in bin(target+1)[3:]: |
| 234 | + node = node and (node.left, node.right)[int(bit)] |
| 235 | + return bool(node) |
| 236 | + |
| 237 | + |
| 238 | + |
| 239 | + |
| 240 | +# Your FindElements object will be instantiated and called as such: |
| 241 | +# obj = FindElements(root) |
| 242 | +# param_1 = obj.find(target) |
| 243 | +``` |
| 244 | + |
| 245 | +## 关键点解析 |
| 246 | + |
| 247 | +- 空间换时间 |
| 248 | +- 二进制思维 |
| 249 | +- 将 target + 1 |
0 commit comments