代码随想录Day20_删除二叉树搜索树中的节点
BG:转眼间已经Day21了,刷代码随想录ing。本人是25届秋招选手,目标后端开发,个人公众号为TccdoeT。关注我,一起交流经验吧!
1.题目描述
https://leetcode.cn/problems/delete-node-in-a-bst/description/
2.题目分析
我认为该题算是二叉搜索树中比较经典的题目了,其实思路很简单,找到并删除,难点在于如何**
正确的分类讨论
来使得删除完后的树
依然组织为一个合法的二叉搜索树
**(注意这个地方,待会会考)。递归法会相对容易,迭代法更考验书写代码的能力,这次也会讲解下迭代法的部分。
首先我们根据题目示例来看大概会存在以下几种情况:
- 删除节点为根节点
- 有左树,无右树
- 有右树,无左树
- 左右子树都无
- 左右子树都有
- 删除节点为非根节点
- 有左树,无右树
- 有右树,无左树
- 左右子树都无
- 左右子树都有
PS:为什么会得到这些分类的,对于我来说,根和非根的分类是比较自然的,至于接下去的小分类想一想也大概想得到.
继续分析下:对于
根和非根分类
的原因是需要考虑其前驱节点的关联
。以下代码是对小类的分类//对于每个小类而言,假设curr为当前需要删除的节点,这种判断的方式真的优雅,可以积累下 //1. if(curr.left==null){ return curr.right; } //2. if(curr.left==null){ return curr.right; } //3.左右子树均不为空 TODO
综上:对于根节点我们直接进行操作就好,分根节点需要前驱节点进行指向,待删除节点的左右子树都存在先放着。我们可以得到如下代码
2.1递归法
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return root;
//1.第一次遍历到此部分就是对根节点处理
if (root.val == key) {
if (root.left == null) {
return root.right;
if (root.right == null) {
return root.left;
}
//2.TODO:左右子树均存在的情况
}
//后续这里需要明确指向关系,想一想是如何操作的
if (root.val > key) root.left = deleteNode(root.left, key);
if (root.val < key) root.right = deleteNode(root.right, key);
return root;
}
}
重点:考虑对待删除节点的左右子树都存在的情况。注意题目只要求变化后的二叉搜索树合法即可,不需要考虑深度或者平衡等等特性,这给我们的操作带来了极大的便利
假设我们需要删除 10 这个节点
可以有以下两种调整的做法,思考一下,你悟了吗
> //假设我们选择左边这种方式,将待删除节点的左孩子接到待删除节点的右孩子的最左孩子。观察以下代码
> TreeNode cur = root.right;
> //找到右子树的最左孩子
> while (cur.left != null) {
> cur = cur.left;
> }
> //连接
> cur.left = root.left;
> //返回待删除节点的右子树
> return root.right;
>
2.2迭代法[冗长,但是好理解]
迭代法是在很难写,难点在哪里,难在我们在删除节点后,需要保留它的前驱关系。观看下面这张图,假设需要删除node.val = 5,删除节点的方法和上面相同,但我们还需要让其前驱节点指向他,并且左右子树指向的操作不一样!使用递归法只需要两个指向即可,迭代法需要自己来维护前一个节点
解决方案:既然我们需要记录当前节点的前一个,那使用栈这种结构不是刚刚好!当遍历到5需要删除时,pop()出10来保证指向关系
class Solution { public TreeNode deleteNode(TreeNode root, int key) { if(root==null){ return null; } //1.对根节点进行特判 if(root.val==key){ if(root.left==null){ return root.right; } if(root.right==null){ return root.left; } //如果都不为空的话:尽量找到叶子节点然后再进行操作 TreeNode node=root.right; while(node.left!=null){ node=node.left; } node.left=root.left; return root.right; } //2.对非根节点进行处理 TreeNode tmp=root; Deque<TreeNode> stack=new LinkedList<>();//利用栈来存储上一个节点 while(root!=null){ //2.1如果找到了需要删除的节点 if(root.val == key){ //2.1.1如果左子树为空 if(root.left==null){ TreeNode node=stack.pop(); //2.1.1.1这里要判断的是我们要删除的节点是前驱节点的左孩子还是右孩子,因为操作是不一致的 if(node.left==root){ node.left=root.right; }else if(node.right==root){ node.right=root.right; } return tmp; } //2.1.2如果右子树为空 if(root.right==null){ TreeNode node=stack.pop(); //2.1.2.1同上 if(node.left==root){ node.left=root.left; }else if(node.right==root){ node.right=root.left; } return tmp; } //2.1.3如果左右子树都不会空 TreeNode node=stack.pop(); //2.1.3.1同上 if(node.left==root){ TreeNode x=root.right; while(x.left!=null){ x=x.left; } x.left=root.left; node.left=root.right; }else if(node.right==root){ TreeNode x=root.right; while(x.left!=null){ x=x.left; } x.left=root.left; node.right=root.right; } return tmp; }else if(root.val > key){ //2.2没找到就push当前节点,并继续 stack.push(root); root=root.left; }else if(root.val < key){ //2.2没找到就push当前节点,并继续 stack.push(root); root=root.right; } } return tmp; } }
以上就是本题的全部内容了,如果对你有帮助可以留言鼓励下哦## 删除二叉树搜索树中的节点