AVL树
如果一个数组{1,2,3,4,5}构建一颗二叉排序树那么他的高度将会很高,导致树的左边为空;虽然说添加的速度影响不大,但是查找速度大大减慢。
上述问题的解决方案就是AVL树。
概念:
1、AVL树也叫平衡二叉树也叫平衡二叉搜索树,可以保证查询效率很高。
2、他的左右两个子树的高度差的绝对值不超过1,并且左右两颗子树都是一个平衡二叉树。
代码中有详细注释,大家不妨先把代码拷贝然后再走一遍
package com.sgr.avl;
/**
* @author 科比
*/
public class AvlTreeDemo {
public static void main(String[] args) {
int[] arr = { 10, 11, 7, 6, 8, 9 };
AvlTree avlTree = new AvlTree();
for(int i=0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
System.out.println("中序遍历");
avlTree.infixOrder();
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());
}
}
class AvlTree{
private Node root;
public Node getRoot(){
return root;
}
/**
* 添加
* @param node
*/
public void add(Node node){
//如果当前树的根节点为空 则要添加的节点就为树的根节点
if(root == null) {
root = node;
}else {
//否则就递归添加
root.add(node);
}
}
public void infixOrder(){
if (root != null){
root.infixOrder();
}else {
System.out.println("二叉排序树为空,不能遍历");
}
}
}
class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
/**
* 返回根节点的左子树的高度
* @return
*/
public int leftHeight(){
if (left == null){
return 0;
}
return left.height();
}
/**
* 返回根节点的左子树的高度
* @return
*/
public int rightHeight(){
if (right == null){
return 0;
}
return right.height();
}
/**
* 返回以改节点为根节点的树的高度
* @return
*/
public int height(){
return Math.max(left == null ? 0: left.height(), right == null ? 0: right.height() ) + 1;
}
/**
* 左旋转流程
* 如果右子树的高度比左子树的高度高 大于1 右子树进行左旋转降低右子树的高度
*/
private void leftRotate(){
//1、以当前节点的值为value创建一个新的节点newNode
Node newNode = new Node(value);
//2、将新节点的左子树设置为当前节点的左子树
newNode.left = left;
//3、将新节点的右子树设置为当前节点的右子树的右子树
newNode.right = right.left;
//4、将当前节点的值设置为当前节点右子树的值
value = right.value;
//5、将当前节点的右子树设置为当前节点的右子树的右子树(当前节点的右子树被置空会被GC掉但是值已经被保存了下来)
right = right.right;
//6、将当前节点的左子树设置为新节点
left = newNode;
}
/**
* 左旋转流程
* 如果左子树的高度比右子树的高度 大于1 左子树进行右旋转降低左子树的高度
*/
private void rightRotate(){
//1、以当前节点的值 创建一个新节点
Node newNode = new Node(value);
//2、新节点的右子树设置为当前节点的右子树
newNode.right = right;
//3、新节点的左子树设置为当前节点的左子树的左子树
newNode.left = left.right;
//4、当前节点的value设置为当前节点左子树的value(当前节点的左子树被置空了GC但是值已经被保存了下来)
value = left.value;
//5、当前节点的左子树设置为当前节点的左子树的左子树
left = left.left;
//6、当前节点的右子树设置为新节点
right = newNode;
}
/**
* 添加节点的方法 递归的进行添加
* @param node 要添加的节点
*/
public void add(Node node){
//如果节点为空则直接返回
if (node == null){
return;
}
//如果要加入的节点的值 小于当前节点那就证明插入节点应在当前节点的左子树
if (node.value < this.value){
//如果当前节点的左子树为空则插入节点就为当前节点的左子树
if (this.left == null){
this.left = node;
}else{
//不为空 就当前节点的左子树继续递归的add插入节点
this.left.add(node);
}
}else {
//否则就证明要插入节点在当前节点的右侧
//如果右子树为空 则 插入节点直接为当前节点的右节点
if (this.right == null){
this.right = node;
}else{
//否则继续右子树递归插入
this.right.add(node);
}
}
/**
* 双旋转
*/
//这个判断是证明右子树高左子树低 要进行左旋转
if (rightHeight() - leftHeight() > 1){
//这个判断是 如果以当前节点右子树为根节点的树 左边比右边高 要进行右旋转
if (right != null && right.leftHeight() > right.rightHeight()){
//先以当前节点的右节点为根进行右旋转
right.rightRotate();
//然后再进行左旋转
leftRotate();
}else {
//否则的话 就直接对当前节点进行左旋转
leftRotate();
}
//这个返回防止下面的代码执行从而导致错误
return;
}
//这个判断是 如果当前节点的左子树高于右子树 需要进行右旋转
if (leftHeight() - rightHeight() > 1){
//这个判断是 以当前节点的左子节点为根节点的 右子树的高度大于左子树的高度 需要进行左旋转
if (left != null && left.rightHeight() > left.leftHeight()){
//先以当前节点的左子节点为根节点进行一次 左旋转
left.leftRotate();
//然后在进行右旋转
rightRotate();
}else {
//否则的话就直接对代码进行右旋转
rightRotate();
}
}
}
public void infixOrder(){
if (this.left != null){
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null){
this.right.infixOrder();
}
}
}