Leetcode 450. Eliminación de nodos en el método iterativo simplificado por árbol de búsqueda binaria

Dada la raíz del nodo raíz de un árbol de búsqueda binaria y una clave de valor, elimine el nodo correspondiente a la clave en el árbol de búsqueda binaria y asegúrese de que la naturaleza del árbol de búsqueda binaria permanezca sin cambios. Devuelve una referencia al nodo raíz del árbol de búsqueda binaria (que puede actualizarse).

En términos generales, la eliminación de un nodo se puede dividir en dos pasos:

Primero busque el nodo que necesita ser eliminado;
si lo encuentra, elimínelo.
Explicación: Se requiere que la complejidad temporal del algoritmo sea O (h) y h es la altura del árbol.

Ejemplo:

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7

El problema es simplemente eliminar un nodo en el árbol de búsqueda binaria.

Uno, método iterativo

Si lo consideras según la situación, la idea es clara y sencilla:

  1. Cuando el nodo que se va a eliminar no tiene nodos secundarios:
    entonces establezca directamente el puntero al nodo que se va a eliminar y déjelo en blanco
  2. Cuando el nodo que se eliminará tiene un nodo secundario:
    apunte el puntero al nodo que se eliminará al hijo del nodo que se eliminará (debe juzgar si es el hijo izquierdo o el hijo derecho)
  3. El nodo que se eliminará tiene dos nodos secundarios:
    busque el valor mínimo en el subárbol derecho del nodo que se eliminará y reemplace el nodo que se eliminará.

Sabemos que si desea eliminar un nodo, primero debe encontrar este nodo. Si lo encuentra iterativamente, debe usar dos punteros, uno para la ubicación que está buscando y el otro para registrar el nodo principal de esta ubicación ( porque al eliminar un nodo, necesita conocer el nodo padre de este nodo)

Entonces, el código de Java es el siguiente:

(1) Encuentre el punto a borrar:

TreeNode p=root;//利用p去遍历,保留root不动,最后得返回root
TreeNode pp=null;//p的父节点

while(p!=null&&p.val!=key){
    
    
    pp=p;
    if(key<p.val) p=p.left;
    else p=p.right;
}//此循环结束后,p就是待删除的节点,pp是p的父节点

if(p==null) return root;//没有找到待删除的值

(2) El nodo que se va a eliminar no tiene nodos secundarios

//如果p没有孩子,则直接把pp指向置空
if(p.left==null&&p.right==null) {
    
    
if(pp.left==p) pp.left=null;
if(pp.right==p) pp.right=null;
}

(3) Cuando el nodo que se va a eliminar tiene un nodo hijo:

 //如果p只有一个孩子,那直接把孩子换上来即可
TreeNode child=null;
if(p.left==null&&p.right!=null) child=p.right;
if(p.left!=null&&p.right==null) child=p.left;
if(pp.left==p) pp.left=child;
if(pp.right==p) pp.right=child;

(4) El nodo que se va a eliminar tiene dos nodos secundarios:

//如果p有左右子树,那就将其右子树的最小值来替换p
        if(p.left!=null&&p.right!=null){
    
    
            TreeNode rp=p.right;
            TreeNode rpp=p;
            while(rp.left!=null){
    
    
                rpp=rp;
                rp=rp.left;
            }//循环结束后,rp就是要找的右子树的最小值,rpp是其父节点
            //将rpp指向rp的指针置空
            if(rpp.left==rp) rpp.left=null;
            if(rpp.right==rp) rpp.right=null;
            //将待删除节点的val换上
            p.val=rp.val;
        }

Pero, al igual que lo anterior, hay muchos si para juzgar las distintas situaciones que se presentan, lo cual es muy engorroso.
Encontré la redacción condensada del Gran Dios:
Al observar lo anterior, debe haber un proceso de vaciar el puntero y volver a apuntar el puntero.
Entonces, el método del Gran Dios es combinar estos dos pasos, de modo que las tres situaciones compartan un conjunto de punteros en el proceso de vaciado y reorientación.
El código de Java es el siguiente:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public TreeNode deleteNode(TreeNode root, int key) {
    
    
        TreeNode p=root;//利用p去遍历,保留root不动,最后得返回root
        TreeNode pp=null;//p的父节点

        while(p!=null&&p.val!=key){
    
    
            pp=p;
            if(key<p.val) p=p.left;
            else p=p.right;
        }//此循环结束后,p就是待删除的节点,pp是p的父节点
        
        if(p==null) return root;//没有找到待删除的值



        //如果p有左右子树,那就将其右子树的最小值来替换p
        if(p.left!=null&&p.right!=null){
    
    
            TreeNode rp=p.right;
            TreeNode rpp=p;
            while(rp.left!=null){
    
    
                rpp=rp;
                rp=rp.left;
            }//循环结束后,rp就是要找的右子树的最小值,rpp是其父节点
            
            //将待删除节点的val换上
            p.val=rp.val;
            //将rpp指向rp的指针置空
            //if(rpp.left==rp) rpp.left=null;
            //if(rpp.right==rp) rpp.right=null;
            p=rp;
            pp=rpp;
        }
        //!!!此方法的这里不好理解!!!
        //删除节点是叶子节点或仅有一个子节点
        TreeNode child;//p的子节点
        if(p.left!=null) child=p.left;
        else if(p.right!=null) child=p.right;
        else child=null;

        if(pp==null) root=child;//删除的是根节点
        else if(pp.left==p) pp.left=child;
        else pp.right=child;

        return root;
        
    }
}

Dos, método recursivo

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
    
    public TreeNode deleteNode(TreeNode root, int key) {
    
    
        if(root==null) return null;

        if(key<root.val){
    
    
            root.left=deleteNode(root.left,key);
        }else if(key>root.val){
    
    
            root.right=deleteNode(root.right,key);
        }else{
    
    //root节点正是待删除的值
            if(root.left==null){
    
    //当前节点没有左子树
                return root.right;
            }else if(root.right==null){
    
    
                return root.left;
            }else{
    
    //当前节点有左右子树,将左子树直接放到右子树的最小节点左侧即可
                //找右子树的最小节点
                TreeNode minP=root.right;
                while(minP.left!=null){
    
    
                    minP=minP.left;
                }//minP即为右子树的最小节点
                minP.left=root.left;
                return root.right;
            }
        }
        return root;       
    }
}

Supongo que te gusta

Origin blog.csdn.net/xiaohaigary/article/details/112290753
Recomendado
Clasificación