AVL树 05 删除节点

从AVL树中删除节点

  • 在子树中删除元素后,返回的根节点链在node节点后,可能对node的平衡性造成破坏,需要对node的平衡性做检查,当平衡性被打破的时候,需要重新调整node的平衡性,调整完成后才能将结果返回到递归的上一层;
  • 在removeMin中可能会打破该树的平衡性,改成递归调用remove(node.right, successor.key);因为remove方法中已经有了维护平衡性的操作;
  • 因为在普通的二分搜索树中,当待删除的节点就是node的情况下会分3中情况讨论:node的右子树为空,node的左子树为空,node左右子树都不为空;前2中情况在node和其子树断开关系后直接返回其左孩子或右孩子,作为新二叉树的根,方法调用结束了;但是在AVL树中,删除node后新树的根不能直接返回,需要重新计算其平衡因子,并在平衡性被打破的时候维护平衡性;所以原先并列的if逻辑需要变成互斥的if...else if...else...;
  • 如果删除的是叶子节点,节点删除后得到的节点是null,那么不用做平衡性的检查和维护,直接返回null即可;
// 从二分搜索树中删除键为key的节点
public V remove(K key){

    Node node = getNode(root, key);
    if(node != null){
        root = remove(root, key);
        return node.value;
    }
    return null;
}

private Node remove(Node node, K key){

    if( node == null )
        return null;

    Node retNode;
    if( key.compareTo(node.key) < 0 ){
        node.left = remove(node.left , key);
        // return node;
        retNode = node;
    }
    else if(key.compareTo(node.key) > 0 ){
        node.right = remove(node.right, key);
        // return node;
        retNode = node;
    }
    else{   // key.compareTo(node.key) == 0

        // 待删除节点左子树为空的情况
        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size --;
            // return rightNode;
            retNode = rightNode;
        }

        // 待删除节点右子树为空的情况
        else if(node.right == null){
            Node leftNode = node.left;
            node.left = null;
            size --;
            // return leftNode;
            retNode = leftNode;
        }

        // 待删除节点左右子树均不为空的情况
        else{
            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
            // 用这个节点顶替待删除节点的位置
            Node successor = minimum(node.right);
            //successor.right = removeMin(node.right);
            successor.right = remove(node.right, successor.key);
            successor.left = node.left;

            node.left = node.right = null;

            // return successor;
            retNode = successor;
        }
    }

    if(retNode == null)
        return null;

    // 更新height
    retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));

    // 计算平衡因子
    int balanceFactor = getBalanceFactor(retNode);

    // 平衡维护
    // LL
    if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
        return rightRotate(retNode);

    // RR
    if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
        return leftRotate(retNode);

    // LR
    if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
        retNode.left = leftRotate(retNode.left);
        return rightRotate(retNode);
    }

    // RL
    if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
        retNode.right = rightRotate(retNode.right);
        return leftRotate(retNode);
    }

    return retNode;
}

remove方法的测试

  • 依次删除AVL树中的所有单词,每次删除完都判断一下AVL树是否是二分搜索树和是否平衡;
public static void main(String[] args){

    System.out.println("Pride and Prejudice");
    ArrayList words = new ArrayList<>();
    if(FileOperation.readFile("pride-and-prejudice.txt", words)) {
        System.out.println("Total words: " + words.size());

        AVLTree map = new AVLTree<>();
        for (String word : words) {
            if (map.contains(word))
                map.set(word, map.get(word) + 1);
            else
                map.add(word, 1);
        }

        System.out.println("Total different words: " + map.getSize());
        System.out.println("Frequency of PRIDE: " + map.get("pride"));
        System.out.println("Frequency of PREJUDICE: " + map.get("prejudice"));

        System.out.println("is BST : " + map.isBST());
        System.out.println("is Balanced : " + map.isBalanced());

        for(String word: words){
            map.remove(word);
            if(!map.isBST() || !map.isBalanced())
                throw new RuntimeException();
        }
    }

    System.out.println();
}

输出:

Pride and Prejudice
Total words: 125901
Total different words: 6530
Frequency of PRIDE: 53
Frequency of PREJUDICE: 11
is BST : true
is Balanced : true

你可能感兴趣的:(AVL树 05 删除节点)