从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<String> words = new ArrayList<>();
if(FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
AVLTree<String, Integer> 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