平衡二叉树:平衡二叉树的数据结构特点是父节点的左右孩子之间的树的高度的差值不超过1。像堆和线性树都是平衡二叉树,但是对于二叉搜索树来说,如果数据的进入顺序不一样,树可能退化成链表。所以需要在创建二叉搜索树的时候对树的状态进行维护。对于创建过程中,可能出现的不平衡的现象总共可以分为四种情况。分别为LL;RR;LR;RL。
我认为旋转的记忆可以想成:往哪边旋转哪边变高,另一边变低。比如当LL的时候,说明左边高,所以需要右旋转使右边变高,左边变低。
LL:加入节点的时候是因为加在父节点的左孩子的左孩子的位置,此时需要进行的操作是进行右旋转。右旋转进行的操作的核心是先存下父节点记为X,然后记录下父节点的左孩子Y的右节点Z。然后令Y的右孩子为X,X的左孩子为Z 大小顺序:D<Y<Z<X
RR:加入节点的时候是因为加在父节点的右孩子的右孩子的位置。此时的操作和LL的操作是对称的。需要进行左旋转:首先记录下父节点X,然后记录下父节点的右节点Y的左孩子Z。然后令Y的左孩子为X,X的右孩子为Z。大小顺序为:X<Z<Y<D
LR:这是因为在加入节点的时候加入到了父节点左孩子的右孩子上了。这个时候就可以先对父节点的左节点进行左旋转使其变成LL,然后再在LL的基础上进行RR旋转。
RL:这是因为在加入节点的时候加入到了父节点右孩子的左孩子上了。这个时候就可以先对父节点的右孩子进行右旋转使其变成RR,然后再在RR的基础上进行左旋转。
谨记:旋转后一定要对旋转的的节点进行高度值的更新。
public class AvlNode {
//定义元素
public int val;
//定义左孩子
public AvlNode left;
//定义右孩子
public AvlNode right;
//定义树的高度
public int height;
//构造函数
public AvlNode(int val,AvlNode left,AvlNode right) {
this.val = val;
this.left = left;
this.right = right;
this.height = 0;
}
//只传值的构造
public AvlNode(int val) {
this(val,null,null);
}
}
import java.util.LinkedList;
import java.util.Queue;
public class AVLTree {
//定义根节点
public AvlNode root;
//定义节点数量
private int size;
//无参构造
public AVLTree() {
root = null;
}
//获得节点的个数
public int getSize() {
return size;
}
//判断是否为空
public boolean isEmpty() {
return size == 0;
}
//获取节点的高度 空叶子节点的高度为-1 叶子节点的高度为0
private int getHeight(AvlNode node) {
return node == null? -1: node.height;
}
//添加元素
public void add(int val) {
root = insert(val,root);
}
private AvlNode insert(int val,AvlNode root) {
if(root == null) {
size ++;
return new AvlNode(val,null,null);
}
int temp = root.val;
if(val < temp) {
root.left = insert(val,root.left);
}
else if(val > temp) {
root.right = insert(val,root.right);
}
return balance(root);
}
private AvlNode balance(AvlNode root) {
if(root == null) {
return root;
}
//左边的情况
if(getHeight(root.left) - getHeight(root.right) > 1) {
if(getHeight(root.left.left) > getHeight( root.left.right)) {
//说明此时是LL的情况,直接进行右旋转
root = singleRotateRight(root);
}
else {
//说明此时是LR的情况,先进行左旋转,然后在进行右旋转
root = doubleRotateLeftRight(root);
}
}
//右边的情况
else if(getHeight(root.right) - getHeight(root.left) > 1){
//如果是RR的情况,要进行左旋转
if(getHeight(root.right.right) > getHeight(root.right.left)) {
root = singleRotateLeft(root);
}
//如果是RL的情况,要先进行右旋转,在进行左旋转
else {
root = doubleRotateRightLeft(root);
}
}
//这条不能忘,因为如果没有需要旋转的情况时也要对树进行一次height的更新。
root.height = Math.max(getHeight(root.left), getHeight(root.right))+1;
return root;
}
//进行单次的右旋转
private AvlNode singleRotateRight(AvlNode root) {
AvlNode node = root.left;
root.left = node.right;
node.right = root;
root.height = Math.max(getHeight(root.left), getHeight(root.right))+1;
node.height = Math.max(getHeight(node.left), root.height) +1;
return node;
}
//进行单词的左旋转
private AvlNode singleRotateLeft(AvlNode root) {
AvlNode node = root.right;
root.right = node.left;
node.left = root;
root.height = Math.max(getHeight(root.left), getHeight(root.right))+1;
node.height = Math.max(getHeight(node.right), root.height) +1;
return node;
}
//x先进行左旋转,在进行右旋转
private AvlNode doubleRotateLeftRight(AvlNode root) {
root.left = singleRotateLeft(root.left);
root = singleRotateRight(root);
return root;
}
//先进行右旋转,在进行左旋转
private AvlNode doubleRotateRightLeft(AvlNode root) {
root.right = singleRotateRight(root.right);
root = singleRotateLeft(root);
return root;
}
//为了检验是否为AVL树
public String toString() {
StringBuilder res = new StringBuilder();
res.append("tree:");
int height = 0;
Queue<AvlNode> queue = new LinkedList<>();
queue.add(root);
AvlNode node = null;
while(!queue.isEmpty()) {
node = queue.poll();
if(node != null) {
res.append(node.val+",");
queue.add(node.left);
queue.add(node.right);
height ++;
}
}
res.append("-----"+height);
return res.toString();
}
public static void main(String[] args) {
AVLTree tree = new AVLTree();
tree.add(3);
tree.add(2);
tree.add(1);
tree.add(4);
tree.add(5);
tree.add(6);
tree.add(7);
tree.add(16);
tree.add(15);
tree.add(14);
tree.add(13);
tree.add(12);
tree.add(11);
tree.add(10);
tree.add(8);
tree.add(9);
System.out.println(tree);
}
}
如果看层次输出的话,应该能是这个顺序。