数据结构之Java实现AVL Tree

AVL Tree是最早的自平衡二分搜索树结构。平衡二叉树对于任意结点左子树与右子树高度差不超过1,平衡二叉树高度与结点数量为O(log n)关系。每一个结点需要记录每一个结点的高度,计算平衡因子,平衡因子等于左子树高度减去右子树高度。对于一个二分搜索树,当添加元素时会破坏平衡,这时需要维护,删除元素时也会造成平衡破坏,需要维护平衡。主要有四种情况,分别如下

1.LL,平衡因子大于1,左子树平衡因子大于等于0,需要将y进行顺时针向下右旋转,x作为父结点

 右旋转,首先保存x右子树T3,将y作为x的右子树,T3作为y的左孩子,需要更新x,y的高度值,完成旋转。

2.RR ,平衡因子小于-1,右子树平衡因子小于等于0,需要将y进行顺时针向下左旋转,x作为父结点

 左旋转,首先保存x左子树T2,将y作为x的左子树,T2作为y的右孩子,需要更新x,y的高度值,完成旋转。

3.LR,平衡因子大于1,左子树平衡因子小于0,首先将x进行左旋转,在将y进行右旋转

 4.RL,平衡因子大于-1,右子树平衡因子大于0,首先将x进行右旋转,在将y进行左旋转

对于删除过程也是会产生上面四种情况,删除完元素也是 一样的。以下是实现过程

import java.util.ArrayList;

public class AVLTree<K extends Comparable<K>, V> {

 private class Node { //声明私有结点内部类
  public K key;  //声明键
  public V value;  //声明值
  public Node left, right; //声明左右结点
  public int height; //结点高度
  //有参构造函数,根据键值对构造相应结点,初始高度为1
  public Node(K key, V value) {
   this.key = key;
   this.value = value;
   left = null;
   right = null;
   height = 1;
  }
 }

 private Node root; //声明根结点
 private int size;  //声明尺寸大小

 public AVLTree() { //无参构造函数
  root = null;  
  size = 0;
 }

 public int getSize() { //获取树大小
  return size;
 }

 public boolean isEmpty() { //判断树是否为空
  return size == 0;
 }
 //判断树是否为二分搜索树,利用二分搜索树中序遍历后是有序的性质
 public boolean isBST() { 
  ArrayList<K> keys = new ArrayList<>();
  inOrder(root, keys);
  for (int i = 1; i < keys.size(); i++) {
   if (keys.get(i - 1).compareTo(keys.get(i)) > 0)
    return false;
  }
  return true;
 }
 //中序遍历
 private void inOrder(Node node, ArrayList<K> keys) {
  if (node == null)
   return;
  inOrder(node.left, keys);
  keys.add(node.key);
  inOrder(node.right, keys);
 }
 //判断树是否平衡,调用私有判断函数
 public boolean isBalanced() {
  return isBalanced(root);
 }
 //判断树是否平衡,采用递归思想,计算每一个结点的平衡因子
 private boolean isBalanced(Node node) {
  if (node == null)
   return true;
  int balanceFactor = getBalanceFactor(node);
  if (Math.abs(balanceFactor) > 1)
   return false;
  return isBalanced(node.left) && isBalanced(node.right);
 }
 //获取结点高度
 private int getHeight(Node node) {
  if (node == null)
   return 0;
  return node.height;
 }
 //获取结点平衡因子,采用递归思想
 private int getBalanceFactor(Node node) {
  if (node == null)
   return 0;
  return getHeight(node.left) - getHeight(node.right);
 }

 //右旋转函数
 private Node rightRotate(Node y) {
  Node x = y.left;
  Node T3 = x.right;

  x.right = y;
  y.left = T3;

  y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
  x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

  return x;
 }

 //左旋转函数
 private Node leftRotate(Node y) {
  Node x = y.right;
  Node T2 = x.left;

  x.left = y;
  y.right = T2;

  y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
  x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

  return x;
 }
 //添加元素,调用私有添加函数
 public void add(K key, V value) {
  root = add(root, key, value);
 }
 //添加函数,采用递归思想添加元素,在添加完元素后,维护高度,计算平衡因子,维护平衡
 private Node add(Node node, K key, V value) {
  if (node == null) {
   size++;
   return new Node(key, value);
  }
  if (key.compareTo(node.key) < 0)
   node.left = add(node.left, key, value);
  else if (key.compareTo(node.key) > 0)
   node.right = add(node.right, key, value);
  else
   node.value = value;
  //当前结点高度等于左右子树最大高度加1
  node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
  int balanceFactor = getBalanceFactor(node);

  // 平衡维护
  // LL
  if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
   return rightRotate(node);

  // RR
  if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)
   return leftRotate(node);

  // LR
  if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {
   node.left = leftRotate(node.left);
   return rightRotate(node);
  }

  // RL
  if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {
   node.right = rightRotate(node.right);
   return leftRotate(node);
  }

  return node; //返回维护好的结点
 }

 //获取相应结点
 private Node getNode(Node node, K key) {
  if (node == null)
   return null;
  if (key.equals(node.key))
   return node;
  else if (key.compareTo(node.key) < 0)
   return getNode(node.left, key);
  else
   return getNode(node.right, key);
 }

 //根据键查看是否包含相应结点
 public boolean contain(K key) {
  return getNode(root, key) != null;
 }

 //根据键获取该结点值
 public V get(K key) {
  Node node = getNode(root, key);
  return node == null ? null : node.value;
 }
 
 //根据键和值更新相应结点
 public void set(K key, V newValue) {
  Node node = getNode(root, key);
  if (node == null)
   throw new IllegalArgumentException(key + " doesn't exist!");
  node.value = newValue;
 }

 //获取该结点子树中最小结点,一直往左边递归遍历
 private Node minimum(Node node) {
  if (node.left == null)
   return node;
  return minimum(node.left);
 }

 //根据键删除相应结点,并返回相应值,调用相应私有递归函数
 public V remove(K key) {
  Node node = getNode(root, key);
  if (node != null) {
   root = remove(root, key);
   return node.value;
  }
  return null;
 }
 //采用递归思想删除相应元素,保存相应返回结点,维护高度,计算平衡因子,维护平衡
 private Node remove(Node node, K key) {
  if (node == null)
   return null;
  Node retNode;
  if (key.compareTo(node.key) < 0) {
   node.left = remove(node.left, key);
   retNode = node;
  } else if (key.compareTo(node.key) > 0) {
   node.right = remove(node.right, key);
   retNode = node;
  } else {
   if (node.left == null) {
    Node rightNode = node.right;
    node.right = null;
    size--;
    retNode = rightNode;
   } else if (node.right == null) {
    Node leftNode = node.left;
    node.left = null;
    size--;
    retNode = leftNode;
   } else {
   Node successor = minimum(node.right);
   successor.right = remove(node.right, successor.key);
   successor.left = node.left;
   node.left = node.right = null;
   retNode = successor;
   }
  }
  
  if(retNode == null)
   return null;
  
  retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
  int balanceFactor = getBalanceFactor(retNode);

  // 平衡维护
  // LL
  if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
   return rightRotate(retNode);

  // RR
  if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
   return leftRotate(retNode);

  // LR
  if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
   retNode.left = leftRotate(retNode.left);
   return rightRotate(retNode);
  }

  // RL
  if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
   retNode.right = rightRotate(retNode.right);
   return leftRotate(retNode);
  }

  return retNode;
 }
}

以上整个过程就是实现AVL Tree,能够保证二分搜索树自平衡。与前几篇博客一样通过读取文本来比较AVL Tree 与BST的数度,这次让数据按照顺序进入到二者中,测试二者消耗时间。如果按照顺序进入,BST应该退化为链表,速度非常慢,AVL Tree能够自平衡,速度应该很快。下面是测试程序和测试结果

1.文件操作类

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Locale;
import java.io.File;
import java.io.BufferedInputStream;
import java.io.IOException;

public class FileOperation {

    public static boolean readFile(String filename, ArrayList<String> words){

        if (filename == null || words == null){
            System.out.println("filename is null or words is null");
            return false;
        }

        Scanner scanner;

        try {
            File file = new File(filename);
            if(file.exists()){
                FileInputStream fis = new FileInputStream(file);
                scanner = new Scanner(new BufferedInputStream(fis), "UTF-8");
                scanner.useLocale(Locale.ENGLISH);
            }
            else
                return false;
        }
        catch(IOException ioe){
            System.out.println("Cannot open " + filename);
            return false;
        }

        if (scanner.hasNextLine()) {

            String contents = scanner.useDelimiter("\\A").next();

            int start = firstCharacterIndex(contents, 0);
            for (int i = start + 1; i <= contents.length(); )
                if (i == contents.length() || !Character.isLetter(contents.charAt(i))) {
                    String word = contents.substring(start, i).toLowerCase();
                    words.add(word);
                    start = firstCharacterIndex(contents, i);
                    i = start + 1;
                } else
                    i++;
        }

        return true;
    }

    private static int firstCharacterIndex(String s, int start){

        for( int i = start ; i < s.length() ; i ++ )
            if( Character.isLetter(s.charAt(i)) )
                return i;
        return s.length();
    }
}

2.测试类

import java.util.ArrayList;
import java.util.Collections;

public class Main {
 public static void main(String[] args) {
  System.out.println("Pride and Prejudice");

  ArrayList<String> words = new ArrayList<>();
  if (FileOperation.readFile("pride-and-prejudice.txt", words)) {
   System.out.println("Total words: " + words.size());
   Collections.sort(words); //对数据进行排序
   
   long startTime = System.nanoTime();
   BST<String, Integer> bst = new BST<>();
   for (String word : words) {
    if (bst.contain(word))
     bst.set(word, bst.get(word) + 1);
    else
     bst.add(word, 1);
   }
   for (String word : words)
    bst.contain(word);
   long endTime = System.nanoTime();
   double time = (endTime - startTime) / 1000000000.0;
   System.out.println("BST: " + time + " s");

   startTime = System.nanoTime();
   AVLTree<String, Integer> avl = new AVLTree<>();
   for (String word : words) {
    if (avl.contain(word))
     avl.set(word, avl.get(word) + 1);
    else
     avl.add(word, 1);
   }
   for (String word : words)
    avl.contain(word);
   endTime = System.nanoTime();
   time = (endTime - startTime) / 1000000000.0;
   System.out.println("AVL: " + time + " s");
  }
  System.out.println();
 }
}

3.测试结果

 测试结果符合预期,BST非常慢,AVL Tree真对这种有序的数据依然很快。如果对于分布很随机的数据,BST效果好一些,非常随机,BST就不会退化和极度倾斜,此时的AVL Tree反而因为各种判断整体效果会差一些。以上就是对于AVL Tree有实现过程。

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值