|
| 1 | + |
| 2 | +>仰望星空的人,不应该被嘲笑 |
| 3 | +
|
| 4 | +## 题目描述 |
| 5 | +给定一个二叉搜索树的根节点 **root** 和一个值 **key**,删除二叉搜索树中的 **key** 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 |
| 6 | + |
| 7 | +一般来说,删除节点可分为两个步骤: |
| 8 | + |
| 9 | +首先找到需要删除的节点; |
| 10 | +如果找到了,删除它。 |
| 11 | +说明: 要求算法时间复杂度为 O(h),h 为树的高度。 |
| 12 | + |
| 13 | +示例: |
| 14 | + |
| 15 | +```javascript |
| 16 | +root = [5,3,6,2,4,null,7] |
| 17 | +key = 3 |
| 18 | + |
| 19 | + 5 |
| 20 | + / \ |
| 21 | + 3 6 |
| 22 | + / \ \ |
| 23 | +2 4 7 |
| 24 | + |
| 25 | +给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。 |
| 26 | + |
| 27 | +一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。 |
| 28 | + |
| 29 | + 5 |
| 30 | + / \ |
| 31 | + 4 6 |
| 32 | + / \ |
| 33 | +2 7 |
| 34 | + |
| 35 | +另一个正确答案是 [5,2,6,null,4,null,7]。 |
| 36 | + |
| 37 | + 5 |
| 38 | + / \ |
| 39 | + 2 6 |
| 40 | + \ \ |
| 41 | + 4 7 |
| 42 | +``` |
| 43 | + |
| 44 | +来源:力扣(LeetCode) |
| 45 | +链接:https://leetcode-cn.com/problems/delete-node-in-a-bst |
| 46 | +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | +## 解题思路 |
| 52 | +对于这道题,我们必须先了解一下二叉搜索树(BST)的性质,如下: |
| 53 | + |
| 54 | +BST性质 |
| 55 | +- 中序遍历是升序 |
| 56 | +- left小于当前节点,right大于当前节点 |
| 57 | +- 左子树、右子树也要是BST |
| 58 | + |
| 59 | + |
| 60 | +了解了性质之后,我们知道要查找对应 `key` 值,可以与根节点进行比较,如果小于根节点,直接去左子树找就好了,如果大于根节点,直接去右子树找就好了。 |
| 61 | + |
| 62 | +而对于刚好等于根节点的话,我拿着大佬的图解来看看几种情况: |
| 63 | + |
| 64 | + |
| 65 | +第一种情况,如果删除节点仅有右孩子,直接指向右孩子 |
| 66 | + |
| 67 | +第二种情况,如果删除节点仅有左孩子,直接指向左孩子 |
| 68 | + |
| 69 | +第三种情况,如果删除节点左右孩子都有,那么我们按照题意可以有两种删除方法: |
| 70 | + |
| 71 | +① 找到要删除节点左子树的最右边的节点,即前驱的最大值(由BST性质得到)替换当前 root 节点,然后删除这个前驱 |
| 72 | + |
| 73 | +② 找到要删除节点右子树的最左边的节点,即后继最小值(由BST性质得到)替换当前 root 节点,然后删除这个后继 |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +参考 <a href="https://leetcode-cn.com/problems/delete-node-in-a-bst/solution/tu-jie-di-gui-shi-xian-shan-chu-bstzhong-de-jie-di/">lulua> 大佬图解 |
| 79 | + |
| 80 | +代码提供了两种解法,对于前两种情况处理是一样的,只是对于最后一种情况,可以删除前驱的最大值,或者删除后继的最小值。 |
| 81 | + |
| 82 | +**删除前驱的最大值代码** |
| 83 | + |
| 84 | +```javascript |
| 85 | +/** |
| 86 | + * Definition for a binary tree node. |
| 87 | + * function TreeNode(val, left, right) { |
| 88 | + * this.val = (val===undefined ? 0 : val) |
| 89 | + * this.left = (left===undefined ? null : left) |
| 90 | + * this.right = (right===undefined ? null : right) |
| 91 | + * } |
| 92 | + */ |
| 93 | +/** |
| 94 | + * @param {TreeNode} root |
| 95 | + * @param {number} key |
| 96 | + * @return {TreeNode} |
| 97 | + */ |
| 98 | +var deleteNode = function (root, key) { |
| 99 | + if (!root) return null; |
| 100 | + // 判断值是否小于root,小于走左子树,大于走右子树 |
| 101 | + if (key < root.val) { |
| 102 | + root.left = deleteNode(root.left, key); |
| 103 | + } else if (key > root.val) { |
| 104 | + root.right = deleteNode(root.right, key); |
| 105 | + } else { |
| 106 | + // 1.如果删除节点没有左右子树,直接删除即可 |
| 107 | + if (!root.left && !root.right) { |
| 108 | + root = null; |
| 109 | + // 2.如果删除节点仅有左孩子,直接指向左孩子 |
| 110 | + } else if (root.left && !root.right) { |
| 111 | + root = root.left; |
| 112 | + // 3.如果删除节点仅有右孩子,直接指向右孩子 |
| 113 | + } else if (!root.left && root.right) { |
| 114 | + root = root.right; |
| 115 | + } else { |
| 116 | + // 4.如果左右孩子都有,本代码采用方式是将前驱的最大值替换root的值 |
| 117 | + let last = root.left; |
| 118 | + while (last.right) { |
| 119 | + last = last.right; |
| 120 | + } |
| 121 | + root.val = last.val; |
| 122 | + // 然后删除这个前驱最大值节点即可 |
| 123 | + root.left = deleteNode(root.left, last.val); |
| 124 | + } |
| 125 | + } |
| 126 | + return root; |
| 127 | +}; |
| 128 | +``` |
| 129 | + |
| 130 | + |
| 131 | +**删除后继的最小值代码** |
| 132 | + |
| 133 | +```javascript |
| 134 | +/** |
| 135 | + * Definition for a binary tree node. |
| 136 | + * function TreeNode(val, left, right) { |
| 137 | + * this.val = (val===undefined ? 0 : val) |
| 138 | + * this.left = (left===undefined ? null : left) |
| 139 | + * this.right = (right===undefined ? null : right) |
| 140 | + * } |
| 141 | + */ |
| 142 | +/** |
| 143 | + * @param {TreeNode} root |
| 144 | + * @param {number} key |
| 145 | + * @return {TreeNode} |
| 146 | + */ |
| 147 | +var deleteNode = function (root, key) { |
| 148 | + if (!root) return null; |
| 149 | + // 判断值是否小于root,小于走左子树,大于走右子树 |
| 150 | + if (key < root.val) { |
| 151 | + root.left = deleteNode(root.left, key); |
| 152 | + } else if (key > root.val) { |
| 153 | + root.right = deleteNode(root.right, key); |
| 154 | + } else { |
| 155 | + // 1.如果删除节点没有左右子树,直接删除即可 |
| 156 | + if (!root.left && !root.right) { |
| 157 | + root = null; |
| 158 | + // 2.如果删除节点仅有左孩子,直接指向左孩子 |
| 159 | + } else if (root.left && !root.right) { |
| 160 | + root = root.left; |
| 161 | + // 3.如果删除节点仅有右孩子,直接指向右孩子 |
| 162 | + } else if (!root.left && root.right) { |
| 163 | + root = root.right; |
| 164 | + } else { |
| 165 | + // 4.如果左右孩子都有,本代码采用方式是将后继的最小值替换root的值 |
| 166 | + let last = root.right; |
| 167 | + while (last.left) { |
| 168 | + last = last.left; |
| 169 | + } |
| 170 | + root.val = last.val; |
| 171 | + // 然后删除这个后继最小值节点即可 |
| 172 | + root.right = deleteNode(root.right, last.val); |
| 173 | + } |
| 174 | + } |
| 175 | + return root; |
| 176 | +}; |
| 177 | +``` |
| 178 | + |
| 179 | +## 最后 |
| 180 | +文章产出不易,还望各位小伙伴们支持一波! |
| 181 | + |
| 182 | +往期精选: |
| 183 | + |
| 184 | +<a href="https://github.com/Chocolate1999/Front-end-learning-to-organize-notes">小狮子前端の笔记仓库a> |
| 185 | + |
| 186 | +<a href="https://github.com/Chocolate1999/leetcode-javascript">leetcode-javascript:LeetCode 力扣的 JavaScript 解题仓库,前端刷题路线(思维导图)a> |
| 187 | + |
| 188 | +小伙伴们可以在Issues中提交自己的解题代码,🤝 欢迎Contributing,可打卡刷题,Give a ⭐️ if this project helped you! |
| 189 | + |
| 190 | + |
| 191 | +<a href="https://yangchaoyi.vip/">访问超逸の博客a>,方便小伙伴阅读玩耍~ |
| 192 | + |
| 193 | + |
| 194 | + |
| 195 | +```javascript |
| 196 | +学如逆水行舟,不进则退 |
| 197 | +``` |
| 198 | + |
| 199 | + |
0 commit comments