前置知识:二叉树的构建和增删改查-CSDN博客
1.二叉树的删除特点
1.1 基本概念
二叉树的删除操作是指在二叉树中移除某个特定节点,同时需要保持二叉树的基本性质不变。删除操作比插入操作更为复杂,因为需要考虑被删除节点的子树如何重新连接到树上。
1.2 删除操作的三种主要情况
1.2.1 删除叶子节点(无子节点)
-
这是最简单的情况,直接移除该节点即可
-
示例:在二叉树中删除节点5(假设5是叶子节点)
5 / \ 3 7删除后:
3 \ 7
1.2.2 删除只有一个子节点的节点
-
将该节点的子节点提升到被删除节点的位置
-
示例:删除节点3(它只有右子节点7)
5 / \ 3 8 \ 7删除后:
5 / \ 7 8
1.2.3 删除有两个子节点的节点
这是最复杂的情况,通常有以下两种处理方法:
方法一:使用前驱节点(左子树的最大值)
- 找到被删除节点左子树中的最大值节点
- 用这个最大值节点替换被删除节点
- 删除原最大值节点(递归处理)
方法二:使用后继节点(右子树的最小值)
- 找到被删除节点右子树中的最小值节点
- 用这个最小值节点替换被删除节点
- 删除原最小值节点(递归处理)
示例:删除节点5
5
/ \
3 8
/ \ / \
2 4 7 9
使用后继节点方法:
- 找到右子树最小值7
- 用7替换5
- 删除原7节点
结果:
7
/ \
3 8
/ \ / \
2 4 9
2.操作流程
图示操作可见1.2
2.1 除叶子节点
- 定位需要删除的目标节点targetNode
- 查找targetNode的父节点parentNode,并判断其是否存在
- 确定targetNode是parentNode的左子树还是右子树
- 根据节点位置执行删除操作:
- 若为左子节点:将parentNode.lChild置为null
- 若为右子节点:将parentNode.rChild置为null
2.2 删除只有一个子树的节点
- 定位需要删除的目标节点targetNode
- 查找targetNode的父节点parentNode(需判断是否存在)
- 确定targetNode在parentNode中的位置(左子树或右子树)
- 根据targetNode的子节点情况进行处理:
情况一:targetNode是parentNode的左子树
- 若targetNode存在左子节点:parentNode.lChild = targetNode.lChild
- 若targetNode存在右子节点:parentNode.lChild = targetNode.rChild
情况二:targetNode是parentNode的右子树
- 若targetNode存在左子节点:parentNode.rChild = targetNode.lChild
- 若targetNode存在右子节点:parentNode.rChild = targetNode.rChild
2.3 删除有两子树的节点
- 定位目标删除节点targetNode
- 查找targetNode的父节点parentNode(需判断是否存在)
- 在targetNode的右子树中寻找最小值节点
- 用右子树最小值节点的值替换targetNode的值
- 删除右子树中的最小值节点
3. 前置操作
3.1 创建方法用于寻找targetNode
java
public TreeNode findTarget(TreeNode root, Integer target) {
if (root == null || target == null) return null;
TreeNode cur = root;
while (cur != null) {
if (cur.data.equals(target)) {
return cur;
} else if (cur.data < target) {
cur = cur.rChild;
} else {
cur = cur.lChild;
}
}
return null;
}
3.2 创建方法用于去找要删除的节点的父节点
java
public TreeNode findParent(TreeNode root,Integer target){
if(root==null){
return null;
}
if((root.lChild!=null) && (root.lChild.data.equals(target))|| (root.rChild!=null) && (root.rChild.data.equals(target))){
return root;
}else {
if(root.lChild!=null && target<root.data){
return findParent(root.lChild,target);
}else if(root.rChild!=null && target>root.data){
return findParent(root.rChild,target);
}else {
return null;
}
}
}
3.3 寻找右子树的最小节点
java
public int findRightTreeMin(TreeNode node){
while (node.lChild!=null){
node = node.lChild;
}
int min = node.data;
delete(root,min);
return min;
}
4.删除节点
4.1 删除的节点只有一个节点
java
if(root.lChild==null && root.rChild==null){//删除的是叶子节点
root=null;
return;
}
4.2 叶子节点
java
if (targetNode.lChild == null && targetNode.rChild == null) { // 叶子节点
// 确定targetNdoe 是parentNode的左子节点还是右子节点
if (parentNode.lChild != null && parentNode.lChild.data.equals(target)) {
parentNode.lChild = null;
}else if (parentNode.rChild != null && parentNode.rChild.data.equals(target)) {
parentNode.rChild = null;
}
4.3 两个子节点
java
if (targetNode.lChild != null && targetNode.rChild != null) { // 两个子节点的节点
int min = findRightTreeMin(targetNode.rChild);//寻找右子树最小的节点
targetNode.data = min;
}
4.4 只有一个子节点的节点
java
if(parentNode.lChild!=null && parentNode.lChild.data.equals(target)){
// targetNode是parentNode的左子节点
if(targetNode.lChild!=null){//删除的是有子节点的节点
parentNode.lChild = targetNode.lChild;
}else if(targetNode.rChild!=null){//删除的是有子节点的节点
parentNode.lChild = targetNode.rChild;
}
}else if(parentNode.rChild!=null && parentNode.rChild.data.equals(target)){
// targetNode是parentNode的右子节点
if(targetNode.lChild!=null){//删除的是有子节点的节点
parentNode.rChild = targetNode.lChild;
}else if(targetNode.rChild!=null){//删除的是有子节点的节点
parentNode.rChild = targetNode.rChild;
}
}
完整代码:
java
//删除节点
public void delete(TreeNode root,Integer target){
if (root==null){
return;
}
//2.万一要删除的节点只有一个节点
if(root.lChild==null && root.rChild==null){//删除的是叶子节点
root=null;
return;
}
//1.找要删除的节点
TreeNode targetNode = findTarget(root,target);
if(targetNode==null){
return;
}
//3.找到父节点
TreeNode parentNode = findParent(root,target);
// 请你进行讲解
if (targetNode.lChild == null && targetNode.rChild == null) { // 叶子节点
// 确定targetNdoe 是parentNode的左子节点还是右子节点
if (parentNode.lChild != null && parentNode.lChild.data.equals(target)) {
parentNode.lChild = null;
}else if (parentNode.rChild != null && parentNode.rChild.data.equals(target)) {
parentNode.rChild = null;
}
} else if (targetNode.lChild != null && targetNode.rChild != null) { // 两个子节点的节点
int min = findRightTreeMin(targetNode.rChild);//寻找右子树最小的节点
targetNode.data = min;
} else { // 只有一个子节点的节点
// 确定targetNdoe 是parentNode的左子节点还是右子节点
if(parentNode.lChild!=null && parentNode.lChild.data.equals(target)){
// targetNode是parentNode的左子节点
if(targetNode.lChild!=null){//删除的是有子节点的节点
parentNode.lChild = targetNode.lChild;
}else if(targetNode.rChild!=null){//删除的是有子节点的节点
parentNode.lChild = targetNode.rChild;
}
}else if(parentNode.rChild!=null && parentNode.rChild.data.equals(target)){
// targetNode是parentNode的右子节点
if(targetNode.lChild!=null){//删除的是有子节点的节点
parentNode.rChild = targetNode.lChild;
}else if(targetNode.rChild!=null){//删除的是有子节点的节点
parentNode.rChild = targetNode.rChild;
}
}
}
总结:
删除二叉树中的节点需要根据节点类型(叶子节点、单子节点、双子节点)采取不同策略,同时保持二叉树的特性(如二叉搜索树的顺序性)。
删除叶子节点
直接移除该节点,并将其父节点的对应子节点指针置空。无需调整树结构。
删除单子节点
将该节点的唯一子节点提升到被删除节点的位置,保持树的连通性。
删除双子节点
对于有两个子节点的节点,通常采用以下两种方法之一:
方法1:前驱替换法 找到左子树的最大节点(前驱),用其值替换待删除节点,然后递归删除前驱节点。
方法2:后继替换法 找到右子树的最小节点(后继),用其值替换待删除节点,然后递归删除后继节点。
完整删除流程(二叉搜索树示例)
- 定位待删除节点及其父节点
- 根据节点类型选择删除策略
- 维护树结构完整性
- 返回更新后的树根节点