引入
此文章记录我学习的笔记,写的不好见谅。
1.AVL树是特殊的二叉搜索树,除了保持原有的二叉搜索树特性,还会一直保持搜索节点的时间复杂度为log(N)。
2.与二叉搜索树的不同于:节点增加一个高度的变量来记录此时的高度。
3.AVL树核心在于旋转,分为:左旋、右旋、左右旋、右左旋
代码
- Java实现代码
import java.util.Stack;
public class AvlBinaryTree {
/* 插入节点,3种情况
1.往左边插,计算后的高度若失衡,需要右旋或者左右旋转
2.往右边插,计算后的高度若失衡,需要左旋或者右左旋转
3.找到适当位置,把当前节点new出来,并返回去
*/
public AvlTreeNode insert(AvlTreeNode treenode, int x){
if(treenode == null){
treenode = new AvlTreeNode(x);
}else if(x < treenode.val){
// 往左边插入
treenode.leftNode = insert(treenode.leftNode, x);
// 需要计算是否失衡
if(treenode.leftNode != null && treenode.rightNode != null){
// 根据高度来判断是否失衡,往左边插入,一定是左边减去右边高度
if(treenode.leftNode.height - treenode.rightNode.height >= 2){
// 判断插入的节点是否在左边节点的右边,这样需要发生左右旋
if(x > treenode.leftNode.val){
treenode = leftRightRoation(treenode);
}else{
// 若只在左边只需要右旋
treenode = rightRoation(treenode);
}
}
}
}else if(x > treenode.val){
// 往右边插入
treenode.rightNode = insert(treenode.rightNode, x);
// 需要计算是否失衡
if(treenode.leftNode != null && treenode.rightNode != null){
// 根据高度来判断是否失衡,往右边插入,一定是右边减去左边高度
if(treenode.rightNode.height - treenode.leftNode.height >= 2){
// 判断插入的节点是否在右边节点的左边,这样需要发生右左旋
if(x < treenode.rightNode.val){
// 发生右左旋
treenode = rightLeftRoation(treenode);
}else{
// 若只在右边只需要左旋
treenode = leftRoation(treenode);
}
}
}
}
// 更新自己的树高,根据自己左右节点的高度来得到自身的高度
treenode.height = mymax(treenode.leftNode, treenode.rightNode) + 1;
return treenode;
}
// 旋转操作
// 左旋
public AvlTreeNode leftRoation(AvlTreeNode avlnode){
// 总:把当前节点的右节点的左节点当做自己的右孩子,自己当做右节点的左孩子
// 1.提取右节点节点
AvlTreeNode rightnd = avlnode.rightNode;
// 2.将右节点的左节点当做父节点的右孩子
avlnode.rightNode = rightnd.leftNode;
// 3.将父节点当做右节点的左节点
rightnd.leftNode = avlnode;
// 这里要重新更新大小
// 先更新子节点的avlnode,再更新(新)头节点 rightnd
avlnode.height = mymax(avlnode.leftNode, avlnode.rightNode) + 1;
rightnd.height = mymax(rightnd.leftNode, rightnd.rightNode) + 1;
return rightnd;
}
// 右旋
public AvlTreeNode rightRoation(AvlTreeNode avlnode){
// 总:把当前节点的左节点的右节点当做自己的左孩子,自己当做左节点的右孩子
// 1.提取左边节点节点
AvlTreeNode leftnd = avlnode.leftNode;
// 2.将左节点的右节点当做父节点的左孩子
avlnode.leftNode = leftnd.rightNode;
// 3.将父节点当做左节点的右节点
leftnd.rightNode = avlnode;
// 这里要重新更新大小
// 先更新子节点的avlnode,再更新(新)头节点 leftnd的高度
avlnode.height = mymax(avlnode.leftNode, avlnode.rightNode) + 1;
leftnd.height = mymax(leftnd.leftNode, leftnd.rightNode) + 1;
return leftnd;
}
// 右左旋
public AvlTreeNode rightLeftRoation(AvlTreeNode avlnode){
// 两次旋转,第一次:以右子节点右旋,第二次:根节点左旋
avlnode.rightNode = rightRoation(avlnode.rightNode);
avlnode = leftRoation(avlnode);
return avlnode;
}
// 左右旋
public AvlTreeNode leftRightRoation(AvlTreeNode avlnode){
// 两次旋转,第一次:以左子节点左旋,第二次:根节点右旋
avlnode.leftNode = leftRoation(avlnode.leftNode);
avlnode = rightRoation(avlnode);
return avlnode;
}
// 辅助操作
// 处理高度左右节点高度对比
public int mymax(AvlTreeNode left, AvlTreeNode right){
if(left == null && right == null){
return 0;
}else if(left != null && right != null){
return left.height > right.height ? left.height : right.height;
}else if(left == null){
return right.height;
}else if(right == null){
return left.height;
}
return 0;
}
// 遍历
// 中序遍历
public void midorder(AvlTreeNode curnode, boolean isprintheight){
Stack<AvlTreeNode> stack = new Stack<>();
while(curnode != null || !stack.isEmpty()){
// 依次把左子树压入栈
while(curnode != null){
stack.push(curnode);
curnode = curnode.leftNode;
}
// 遍历完左子树,弹出栈顶并访问
curnode = stack.pop();
if(isprintheight){
System.out.print("节点:"+curnode.val+" 的高度:"+curnode.height+"; ");
}else{
System.out.print(curnode.val+" ");
}
// 变换为它的右节点继续中序遍历
curnode = curnode.rightNode;
}
System.out.println();
}
public static void main(String[] args) {
AvlBinaryTree ab = new AvlBinaryTree();
// 构建树,测试发生左旋转的时候
AvlTreeNode root = new AvlTreeNode(6);
root = ab.insert(root, 4);
root = ab.insert(root, 8);
root = ab.insert(root, 7);
root = ab.insert(root, 9);
// root树根在不平衡的时候是会变的,此时增加此10的节点会发生不平衡,以6节点作左旋
root = ab.insert(root, 10);
ab.midorder(root, true);
// 构建树,测试发生右旋转的时候
root = new AvlTreeNode(6);
root = ab.insert(root, 8);
root = ab.insert(root, 4);
root = ab.insert(root, 3);
root = ab.insert(root, 5);
// root树根在不平衡的时候是会变的,此时增加此2的节点会发生不平衡,以6节点作右旋
root = ab.insert(root, 2);
ab.midorder(root, true);
// 构建树,测试发生右左旋转的时候
root = new AvlTreeNode(8);
root = ab.insert(root, 5);
root = ab.insert(root, 16);
root = ab.insert(root, 18);
root = ab.insert(root, 15);
// root树根在不平衡的时候是会变的,此时增加此14的节点会发生不平衡,先以16节点作右旋,再以8左旋
root = ab.insert(root, 14);
ab.midorder(root, true);
// 构建树,测试发生左右旋转的时候
root = new AvlTreeNode(16);
root = ab.insert(root, 18);
root = ab.insert(root, 10);
root = ab.insert(root, 9);
root = ab.insert(root, 11);
// root树根在不平衡的时候是会变的,此时增加此12的节点会发生不平衡,先以10节点作左旋,再以16节点右旋
root = ab.insert(root, 12);
ab.midorder(root, true);
}
}
-
运行中序遍历的结果
-
对应的旋转过程(手画,不是很好看,见谅)