AVL树(带有平衡条件的二叉查找树)
一、给你一个数列{1,2,3,4,5,6},要求创建一棵二叉排序树$BST$树,如下图所示。
该$BST$存在一些问题,如下:
- 左子树全部为空,从形式上看更像一个单链表;
- 查询比单链表慢,因为查询时还要比较左子树;
- 这个BST树是不平衡的,解决方案为改用平衡条件的二叉查找树(AVL);
二、平衡二叉树
平衡二叉树也叫平衡二叉搜索树,也被称为AVL树。它可以保证较高的查询效率。AVL树具有以下特点:它是一棵空树或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL算法(不同于AVL树)、伸展树等。
下面是单旋转的情况,以左旋转为例,图示如下:
这是一个右子树的高度比左子树的高度高很多
左旋转
- 创建一个新的节点newNode,值等于当前根节点的值(以4这个值创建)
- 把新节点的左子树设置为当前节点的左子树
- 把新节点的右子树设置为当前节点的右子树的左子树
- 把当前节点的值替换为其右子节点的值
- 把当前节点的左子树设置为新节点
- 把当前节点的右子树设置为其右子树的右子树
代码如下:
//单旋转(以左旋转为例) public void leftRotate() { //创建新节点,值为当前根节点的值 node newNode = new node(this.number); //把新的节点的左子节点设置成当前节点的左子节点 newNode.left = this.left; //把新节点的右子节点设置为当前节点的右子节点的左子节点 newNode.right = this.right.left; //把当前节点的值替换为其右子节点的值 this.number = this.right.number; //把当前节点的右子节点改为右子节点的右子节点 this.right = this.right.right; //把当前节点的左子节点改为新的节点 this.left = newNode; }
如果是根的左子树比右子树高呢? arr = {10,12,8,9,7,6};
二叉树的左子树的高度比右子树的高度高很多,需要进行右旋转。
- 创建一个新节点,值等于当前根节点的值
- 新节点的右子树为当前节点的右子树
- 新节点的左子树为当前节点的左子树的右子树
- 当前节点的值替换为其左子节点的值
- 当前节点的左子树为其左子树的左子树
- 当前节点的右子树为新的节点
代码:
//单旋转(以右旋转为例) public void rightRotate() { node newNode = new node(this.number); newNode.right = this.right; newNode.left = this.left.right; this.number = this.left.number; this.left = this.left.left; this.right = newNode; }
三、双旋转
在某些情况下,单旋转不能完成平衡二叉树的转换。如下图:
这种情况下,直接进行右旋转,并不能达到平衡的效果。
分析一下:
- 如果根的左子树的右子树大于根的左子树的左子树
- 应该先对当前节点的左子节点进行左旋转
- 然后对当前节点进行右旋转
代码:
if (getRoot().leftHeight() - getRoot().rightHeight() > 1) { //根节点的左子树的高度与右子树的高度差大于1 if (getRoot().left.rightHeight() > getRoot().left.leftHeight()) { //对当前节点的左子节点进行左旋转 getRoot().left.leftRotate(); getRoot().rightRotate(); } else { getRoot().rightRotate(); //右旋转 } return ; //这个加上,避免退出后再进入下一个左旋转 } //左旋转 if (getRoot().rightHeight() - getRoot().leftHeight() > 1) { //根节点的右子树的高度与左子树的高度差大于1 if (getRoot().right.leftHeight() > getRoot().right.rightHeight()) { getRoot().right.rightRotate(); //右子节点进行右旋转 getRoot().leftRotate(); } else { getRoot().leftRotate(); //左旋转 } }
完整代码:
package AVL; import java.util.EmptyStackException; public class AVLTreeDemo { public static void main(String[] args) { int[] arr = {10,11,7,6,8,9}; //创建一个AVLTree对象 AVLTree avltree = new AVLTree(); //添加节点 for (int i = 0; i < arr.length; i++) { avltree.insert(arr[i]); } //遍历 System.out.println("中序遍历:"); avltree.printTree(); //平衡处理后 System.out.println("平衡处理后:"); System.out.println("树的高度:" + avltree.getRoot().height()); System.out.println("左子树的高度:" + avltree.getRoot().leftHeight()); System.out.println("右子树的高度:" + avltree.getRoot().rightHeight()); System.out.println("根节点的值为:" + avltree.getRoot()); System.out.println("根节点的左子节点为:" + avltree.getRoot().left); System.out.println("根节点的右子节点为:" + avltree.getRoot().right); } } class AVLTree { private node root; //根节点就能确定一棵二叉树 public void setRoot(node root) { this.root = root; } public node getRoot() { return root; } //BST的构造函数 public AVLTree() { this.root = null; } //建立空树 public void makeEmpty() { this.root = null; } //判断是否为空 public boolean isEmpty() { return this.root == null; } //判断是否包含某一节点 public boolean contains(int x) { return contains(x, this.root); } //寻找二查搜索树上的最小值 public int findMin() { if (isEmpty()) { throw new EmptyStackException(); } return findMin(root).number; } //寻找二查搜索树上的最大值 public int findMax() { if (isEmpty()) { throw new EmptyStackException(); } return findMax(root).number; } //插入节点 public void insert(int x) { root = insert(x, root); //右旋转 if (getRoot().leftHeight() - getRoot().rightHeight() > 1) { //根节点的左子树的高度与右子树的高度差大于1 if (getRoot().left.rightHeight() > getRoot().left.leftHeight()) { //对当前节点的左子节点进行左旋转 getRoot().left.leftRotate(); getRoot().rightRotate(); } else { getRoot().rightRotate(); //右旋转 } return ; //这个加上,避免退出后再进入下一个左旋转 } //左旋转 if (getRoot().rightHeight() - getRoot().leftHeight() > 1) { //根节点的右子树的高度与左子树的高度差大于1 if (getRoot().right.leftHeight() > getRoot().right.rightHeight()) { getRoot().right.rightRotate(); //右子节点进行右旋转 getRoot().leftRotate(); } else { getRoot().leftRotate(); //左旋转 } } } //删除节点 public void remove(int x) { root = remove(x, root); } //打印二查搜索树(前序遍历) public void printTree() { if (isEmpty()) System.out.println("Empty tree"); else printTree(this.root); } private boolean contains(int x, node t) { //如果t是空集,就返回false if(t == null) { return false; } //注意:compareTo的用法,可以判断两个元素间的三种情况 int compareResult = x - t.number; if (compareResult < 0) { //注意:这里可以用尾递归优化 return contains(x, t.left); } else if (compareResult > 0) { return contains(x, t.right); } else return true; } private node findMin(node t) { //findMin方法的递归实现 if (t == null) return null; else if (t.left == null) return t; return findMin(t.left); } private node findMax(node t) { //findMax方法的非递归实现(while循环,一直寻找最右端的节点) if (t != null) { while (t.right != null) { t = t.right; } } return t; } private node insert(int x, node t) { if (t == null) { return new node(x, null, null); } int compareResult = x - t.number; if (compareResult < 0) { t.left = insert(x, t.left); } else if (compareResult > 0){ t.right = insert(x, t.right); } else System.out.println("The insert Node has already exist, so we will do noting."); return t; } private node remove(int x, node t) { if (t == null) { return t; } int compareResult = x- t.number; if (compareResult < 0) { t.left = remove(x, t.left); } else if(compareResult > 0) { t.right = remove(x, t.right); }else if (t.left != null && t.right != null) { // 有两个子节点 t.number = findMin(t.right).number; //找到右子树上最小的一个节点 t.right = remove(t.number, t.right); }else //至多有一个子节点(如果没有子节点,直接用null删除即可;反之,用子节点替代) t = (t.left != null) ? t.left: t.right; return t; } private void printTree(node t) { //中序遍历 if (t != null) { printTree(t.left); System.out.println(t.number); printTree(t.right); } } //前序遍历 public void preOrder() { if(this.root != null) { this.root.preOrder(); } else { System.out.println("当前二叉树为空,无法进行前序遍历"); } } //中序遍历 public void infixOrder() { if(this.root != null) { this.root.infixOrder(); } else { System.out.println("当前二叉树为空,无法进行中序遍历"); } } //后序遍历 public void postOrder() { if(this.root != null) { this.root.postOrder(); } else { System.out.println("当前二叉树为空,无法进行后序遍历"); } } } //节点 class node { public int number; public node left; public node right; public node(int number, node left, node right) { this.number = number; this.left = left; this.right = right; } public int leftHeight() { if (left == null) { return 0;} return left.height(); } public int rightHeight() { if (right == null) { return 0;} return right.height(); } //以当前节点为根节点的树的高度 public int height() { return Math.max(left == null? 0:left.height(), right == null? 0: right.height()) + 1; //加1表示加上当前节点的高度 } //leftType = 0 说明连接的是左子树;leftType = 1 说明连接的是前驱节点 private int leftType; //rightType = 0 说明连接的是右子树;rightType = 1 说明连接的是后继节点 private int rightType; public node(int number) { this.number = number; } public node getLeft() { return left; } public node getRight() { return right; } public int getLeftType() { return leftType; } public int getRightType() { return rightType; } public void setLeftType(int leftType) { this.leftType = leftType; } public void setRightType(int rightType) { this.rightType = rightType; } public void setNumber(int number) { this.number = number; } public void setLeft(node left) { this.left = left; } public void setRight(node right) { this.right = right; } //输出当前节点的信息 @Override public String toString() { return "Node{" + "number=" + number + '}'; } //前序遍历 public void preOrder() { System.out.println(this); if (this.left != null) { this.left.preOrder(); } if (this.right != null) { this.right.preOrder(); } } //中序遍历 public void infixOrder() { if (this.left != null) { this.left.infixOrder(); } System.out.println(this); if (this.right != null) { this.right.infixOrder(); } } //后序遍历 public void postOrder() { if (this.left != null) { this.left.postOrder(); } if (this.right != null) { this.right.postOrder(); } System.out.println(this); } //单旋转(以左旋转为例) public void leftRotate() { //创建新节点,值为当前根节点的值 node newNode = new node(this.number); //把新的节点的左子节点设置成当前节点的左子节点 newNode.left = this.left; //把新节点的右子节点设置为当前节点的右子节点的左子节点 newNode.right = this.right.left; //把当前节点的值替换为其右子节点的值 this.number = this.right.number; //把当前节点的右子节点改为右子节点的右子节点 this.right = this.right.right; //把当前节点的左子节点改为新的节点 this.left = newNode; } //单旋转(以右旋转为例) public void rightRotate() { node newNode = new node(this.number); newNode.right = this.right; newNode.left = this.left.right; this.number = this.left.number; this.left = this.left.left; this.right = newNode; } }
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!