1,二叉排序树问题
- 对于一个有序数组
{1, 2, 3, 4, 5}
,其生成的二叉排序树如下;由图可见,最终形成一个类似单链表形式的二叉树,对插入速度没有影响,但是对于查询速度明显减低,不能通过BST进行查询,时间复杂度为O(n)
;需要对二叉排序树这种现象进行优化,则引入平衡二叉树(AVL)
2,平衡二叉树基本介绍
- 平衡二叉树也叫平衡二叉搜索树(self-balancing binary search tree),又被称为AVL树,可以保证较高的二分查找效率
- 平衡二叉树特点:是一颗空树或者左右子树的高度差绝对值小于1的树,并且左右子树都是平衡二叉树;平衡二叉树的实现有红黑树,AVL,替罪羊树,Treap,伸展树等
- 平衡二叉树的平衡保证通过单旋转(左旋转,右旋转)和双旋转完成
3,左旋转
- 左旋触发条件:当前节点的左树高度与右树高度差值大于1,进行左旋,重新保证树的平衡
- 将当前节点重新初始化为一个新节点
newNode
- 将
newNode
的右侧节点,设置为当前节点的右侧节点 - 将
newNode
的左侧节点,设置为当前节点左侧节点的右侧节点 - 将当前节点的权值设置为当前节点左侧节点的权值,此时完成左侧节点上浮
- 将当前节点的左侧节点设置为左侧节点的左侧节点,当前节点的原始左侧节点已经上浮,挂空后等待GC回收
- 将当前节点的右侧节点设置为
newNode
- 此时完成左旋,示意图如下,节点40表示当前节点
4,右旋转
- 右旋触发条件:当前节点的右树高度与左树高度差值大于1,进行右旋,重新保证树的平衡
- 将当前节点重新初始化为一个新节点
newNode
- 将
newNode
的左侧节点,设置为当前节点的左侧节点 - 将
newNode
的右侧节点,设置为当前节点右侧节点的左侧节点 - 将当前节点的权值设置为当前节点右侧节点的权值,此时完成右侧节点上浮
- 将当前节点的右侧节点设置为右侧节点的右侧节点,当前节点的原始右侧节点已经上浮,挂空后等待GC回收
- 将当前节点的左侧节点设置为
newNode
- 此时完成右旋,示意图如下,节点40表示当前节点
5,双旋转
- 双旋转触发条件:双旋转是先左旋转,后右旋转(或者相反)。以基本旋转为右旋转分析,左旋转相反
- 当前节点为根节点的二叉树已经满足右旋转标准,但是其左侧子节点的右侧子树高度高于左侧子树;因为在右旋时需要将左侧子节点的右侧子树挂到新建节点的左侧,直接右旋完成后,会发现高度差依然是2,不过是右侧比左侧高2,又符合左旋标准,进入死循环,如图:
-
- 因为左侧子节点在右旋时高度会升高1,其左侧节点如果比其右侧节点高度高1,则旋转后叶子节点高度相同;如果其左侧节点比其右侧节点高度少1,则旋转后,其左侧节点再高一层,右侧高度不变,高差依旧为2,又需要左旋,进入死循环
- 此时处理办法是,先对子树进行旋转;基本旋转为右旋时,如果当前节点左侧子节点的右子树高度高于左子树,则以该左侧节点为根节点,首先进行一次左旋,保证在该子树上,左子树节点高度高于右子树节点;
- 此时再进行基本的右侧旋转,则最终获取到的AVL树就是标准的AVL树
6,代码实现
package com.self.datastructure.tree.binarytree;
import lombok.Data;
public class AVLTree {
public static void main(String[] args) {
MyAVLTree myAVLTree = new MyAVLTree();
}
static class MyAVLTree {
private Node root;
public Node getRoot() {
return root;
}
public void delNode(int value) {
if (null == root) {
return;
}
if (root.getValue() == value && null == root.getLeftNode() && null == root.getRightNode()) {
root = null;
return;
}
doDelNode(null, root, value);
}
private void doDelNode(Node parentNode, Node node, int value) {
deleteNode(parentNode, node, value);
refreshAVLTree(root);
}
private void refreshAVLTree(Node node) {
if (null == node) {
return;
}
refreshAVLTree(node.getLeftNode());
refreshAVLTree(node.getRightNode());
rotate(node);
}
private void deleteNode(Node parentNode, Node node, int value) {
if (node.getValue() < value) {
deleteNode(node, node.getRightNode(), value);
} else if (node.getValue() > value) {
deleteNode(node, node.getLeftNode(), value);
} else {
if (null == node.getLeftNode() && null == node.getRightNode()) {
if (parentNode.getRightNode() == node) {
parentNode.setRightNode(null);
} else if (parentNode.getLeftNode() == node) {
parentNode.setLeftNode(null);
}
node = parentNode;
} else if (null == node.getLeftNode() || null == node.getRightNode()) {
if (null == node.getLeftNode()) {
node.setValue(node.getRightNode().getValue());
node.setRightNode(null);
} else if (null == node.getRightNode()) {
node.setValue(node.getLeftNode().getValue());
node.setLeftNode(null);
}
} else {
Node tmpNode = node.getLeftNode();
parentNode = node;
for (;null != tmpNode.getRightNode();) {
parentNode = tmpNode;
tmpNode = tmpNode.getRightNode();
}
node.setValue(tmpNode.getValue());
if (tmpNode == parentNode.getLeftNode()) {
parentNode.setLeftNode(tmpNode.getLeftNode());
} else if (tmpNode == parentNode.getRightNode()) {
parentNode.setRightNode(tmpNode.getLeftNode());
}
}
}
}
public void addNode(int value) {
if (null == root) {
root = new Node(value);
return;
}
doAddNode(root, value);
}
private void doAddNode(Node parentNode, int value) {
if (null == parentNode) {
return;
}
if (parentNode.getValue() < value) {
if (null == parentNode.getRightNode()) {
parentNode.setRightNode(new Node(value));
} else {
doAddNode(parentNode.getRightNode(), value);
}
} else if (parentNode.getValue() > value) {
if (null == parentNode.getLeftNode()) {
parentNode.setLeftNode(new Node(value));
} else {
doAddNode(parentNode.getLeftNode(), value);
}
}
rotate(parentNode);
}
private void rotate(Node currNode) {
if (getLeftHeight(currNode) - getRightHeight(currNode) > 1) {
if (null != currNode.getLeftNode() && getLeftHeight(currNode.getLeftNode()) < getRightHeight(currNode.getLeftNode())) {
leftRotate(currNode.getLeftNode());
}
rightRotate(currNode);
}
else if (getRightHeight(currNode) - getLeftHeight(currNode) > 1) {
if (null != currNode.getRightNode() && getRightHeight(currNode.getRightNode()) < getLeftHeight(currNode.getRightNode())) {
rightRotate(currNode.getLeftNode());
}
leftRotate(currNode);
}
}
public void rightRotate(Node node) {
Node newNode = new Node(node.getValue());
newNode.setRightNode(node.getRightNode());
newNode.setLeftNode(node.getLeftNode().getRightNode());
node.setValue(node.getLeftNode().getValue());
node.setLeftNode(node.getLeftNode().getLeftNode());
node.setRightNode(newNode);
}
public void leftRotate(Node node) {
Node newNode = new Node(node.getValue());
newNode.setLeftNode(node.getLeftNode());
newNode.setRightNode(node.getRightNode().getLeftNode());
node.setValue(node.getRightNode().getValue());
node.setRightNode(node.getRightNode().getRightNode());
node.setLeftNode(newNode);
}
public int getRightHeight(Node node) {
if (null == node.getRightNode()) {
return 0;
}
return getHeight(node.getRightNode());
}
private int getLeftHeight(Node node) {
if (null == node.getLeftNode()) {
return 0;
}
return getHeight(node.getLeftNode());
}
public int getHeight(Node node) {
int height = 0;
int leftHeight = 0;
int rightHeight = 0;
if (null != node.getLeftNode()) {
leftHeight += getHeight(node.getLeftNode());
}
if (null != node.getRightNode()) {
rightHeight = getHeight(node.getRightNode());
}
height = Math.max(leftHeight, rightHeight);
return height + 1;
}
public void middleShowDetails() {
doMiddleShowDetails(root);
}
private void doMiddleShowDetails(Node node) {
if (null == node) {
return;
}
doMiddleShowDetails(node.getLeftNode());
System.out.println(node);
doMiddleShowDetails(node.getRightNode());
}
}
@Data
static class Node {
private int value;
private Node leftNode;
private Node rightNode;
public Node() {}
public Node(int value) {
this.value = value;
}
public String toString() {
return "Node: [value = " + value + "]";
}
}
}