简体   繁体   English

使用AVL树的地图实现

[英]Map implementation using AVL tree

I am trying to implement a simple Map using an AVL tree as an underlying structure. 我正在尝试使用AVL树作为基础结构来实现一个简单的Map。 I implemented the Map using a binary search tree as the underlying structure, but I am having trouble picturing how to check and balance the tree if necessary. 我使用二进制搜索树作为基础结构实现了Map,但是在描绘必要时如何检查和平衡树的过程中遇到了麻烦。 When I put in a key-value pair, I need to check the balance of the tree and act accordingly. 当我放入一个键值对时,我需要检查树的平衡并采取相应的措施。 I am not sure how to go about doing this. 我不确定该怎么做。 Here is my code for MyAVLMap (leveraged from the binary search tree implementation). 这是我的MyAVLMap代码(来自二进制搜索树实现)。 Any help is appreciated!! 任何帮助表示赞赏!

Here is my class: 这是我的课:

public class MyAVLMap<K, V> implements BasicMap<K, V> {

  // the root of the "tree" that structures the map
  private AVLNode root;

  // the number of key-value mappings currently in the map
  private int numKeys;

  /**
   * Constructs MyAVLMap object.
   */
  public MyAVLMap() {
    root = null;

    numKeys = 0;
  }

  /**
   * Associates the specified value with the specified
   * key in the current map.
   * 
   * @param key The key with which the specified value
   *            is to be associated.
   * @param value The value to be associated with the 
   *              specified key.
   */
  public void put(K key, V value){
    // input validation
    if(key == null || value == null) {
      throw new IllegalArgumentException();
    }

    root = put(root, (Comparable) key, value);
  }

//  // This helper method adds the specified key-value mapping
//  // to the tree rooted at "r".
//  @SuppressWarnings("unchecked")
//  private AVLNode put(AVLNode r, Comparable key, V value){
//    if(r == null) {
//      numKeys++;
//      return new AVLNode(key, value);
//    }
//    
//    int compare = key.compareTo(r.key);
//    if(compare == 0) {
//      r.value = value;
//    } else if(compare < 0) {
//      r.left = put(r.left, key, value);
//    } else {  // (compare > 0)
//      r.right = put(r.right, key, value);
//    }
//    
//    return r;
//  }  

  /**
   * Internal method to insert into a subtree.
   * 
   * @param x the item to insert.
   * @param t the node that roots the tree.
   * @return the new root.
   */
  @SuppressWarnings("unchecked")
  private AVLNode put(AVLNode r, Comparable key, V value)
  {
    if(r == null) {
      return new AVLNode(key, value);
    } else if(key.compareTo(r.key) < 0) {
      r.left = put(r.left, key, value);
      if((r.left.height - r.right.height == 2) &&
         (key.compareTo(r.left.key) < 0)) {
        r = rotateWithLeftChild(r);
      } else {
        r = doubleWithLeftChild(r);
      }
    } else if(key.compareTo(r.key) > 0 ) {
      r.right = put(r.right, key, value);
      if((r.right.height - r.left.height == 2) &&
         (key.compareTo(r.right.key) > 0)) {
        r = rotateWithRightChild(r);
      }
      else {
        r = doubleWithRightChild(r);
      }
    }

    // else: Duplicate; do nothing
    r.height = max(r.left.height, r.right.height) + 1;

    return r;
  }

  // This helper method returns the greater of two integers.
  private static int max(int n1, int n2)
  {
    if(n1 > n2) {
      return n1;
    } else {  // left <= right
      return n2;
    }
  }

  /**
   * Rotate binary tree node with left child.
   * For AVL trees, this is a single rotation for case 1.
   * Update heights, then return new root.
   */
  private AVLNode rotateWithLeftChild(AVLNode root2) {
    AVLNode root1 = root2.left;
    root2.left = root1.right;
    root1.right = root2;

    root2.height = max(root2.left.height, root2.right.height) + 1;
    root1.height = max(root1.left.height, root2.height) + 1;

    return root1;
  }

  /**
   * Rotate binary tree node with right child.
   * For AVL trees, this is a single rotation for case 4.
   * Update heights, then return new root.
   */
  private AVLNode rotateWithRightChild(AVLNode root1) {
    AVLNode root2 = root1.right;
    root1.right = root2.left;
    root2.left = root1;

    root1.height = max(root1.left.height, root1.right.height) + 1;
    root2.height = max(root2.right.height, root1.height) + 1;

    return root2;
  }

  /**
   * Double rotate binary tree node: first left child
   * with its right child; then node k3 with new left child.
   * For AVL trees, this is a double rotation for case 2.
   * Update heights, then return new root.
   */
  private AVLNode doubleWithLeftChild(AVLNode r) {
    r.left = rotateWithRightChild(r.left);

    return rotateWithLeftChild(r);
  }

  /**
   * Double rotate binary tree node: first right child
   * with its left child; then node r1 with new right child.
   * For AVL trees, this is a double rotation for case 3.
   * Update heights, then return new root.
   */
  private AVLNode doubleWithRightChild(AVLNode r) {
    r.right = rotateWithLeftChild(r.right);

    return rotateWithRightChild(r);
  }

  /**
   * Returns the value to which the specified key is mapped,
   * or <tt>null</tt> if the specified key is not mapped.
   * 
   * @param key The key whose associated value is to be returned.
   * @return The value to which the specified key is mapped,
   *         or <tt>null</tt> if the specified key is not mapped.
   */
  public V get(Object key){
    return get(root, (Comparable) key);
  }

  // This helper method retrieves the value associated with the
  // specified key in the tree rooted at "r".
  @SuppressWarnings("unchecked")
  private V get(AVLNode r, Comparable key){
    if(r == null) {
      return null;
    }

    int compare = key.compareTo(r.key);
    if(compare == 0) {
      return (V) r.value;
    } else if(compare < 0) {
      return get(r.left, key);
    } else {  // compare > 0
      return get(r.right, key);
    }
  }

  /**
   * Returns true if the current map contains a mapping for the
   * specified key.
   * 
   * @param key The key whose presence in the map is being tested.
   * @return If this map contains a mapping for the specified
   *        key.
   */
  public boolean containsKey(Object key) {
    return get((Comparable) key) != null;
  }

  /**
   * Returns true if the current map contains no key-value mappings.
   * 
   * @return If the current map contains no key-value mappings.
   */
  public boolean isEmpty() {
    return root == null;
  }

  /**
   * Removes all of the mappings from the current map.
   */
  public void clear() {
    root = null;

    numKeys = 0;
  }

  /**
   * Returns the number of key-value mappings in this map
   * @return The number of key-value mappings in this map
   */
  public int size(){
    return numKeys;
  }

  /**
   * Returns the String representation of this map.
   * 
   * @return The String representation of this map.
   */
  @Override
  public String toString() {
    return "(" + subtreeToString(root) + ")";
  }

  // This helper method returns a String representation of the contents
  // of the map for (sub-)tree with root "r".
  private String subtreeToString(AVLNode r) {
    String str = "";

    if(r == null) {
      return str;
    }

    str += r.key + ":" + r.value;
    str += " (" + subtreeToString(r.left) + ") (" + 
      subtreeToString(r.right) + ")";

    return str;
  }

  // This inner class creates each of the node(s) that
  // compose the AVL tree structure.
  class AVLNode<K, V> {

    /**
     * The key stored in the node.
     */
    public K key;

    /**
     * The corresponding value associated with a key in the node.
     */
    public V value;

    /**
     * The node to the left of "this" node.
     */
    public AVLNode left;

    /**
     * The node to the right of "this" node.
     */
    public AVLNode right;

    /**
     * The height of "this" node.
     */
    public int height;

    /**
     * Constructs the AVLNode object (four parameters).
     * 
     * @param key The key to be stored in the node.
     * @param value The corresponding value to be stored in the node.
     * @param left The node to the left of "this" node.
     * @param right The node to the right of "this" node.
     */
    @SuppressWarnings("unchecked")
    public AVLNode(Object key, Object value, AVLNode left, AVLNode right) {
      // bind to references
      this.key = (K) key;
      this.value = (V) value;
      this.left = left;
      this.right = right;

      height = 0;
    }

    /**
     * Constructs the AVLNode (two parameters).
     * 
     * @param key The key to be stored in the node.
     * @param value The corresponding value to be stored in the node.
     */
    public AVLNode(Object key, Object value) {
      // call three parameter constructor
      this(key, value, null, null);

      height = 0;
    }

  }

}

And here is the interface it implements: 这是它实现的接口:

public interface BasicMap<K, V> {

    /**
     * Associates the specified value with the specified
     * key in the current map. Neither key nor value should
     * be <tt>null</tt>.
     * @param key The key with which the specified value
     * is to be associated.
     * @param value The value to be associated with the 
     * specified key.
     */
    public void put(K key, V value);

    /**
     * Returns the value to which the specified key is mapped,
     * or <tt>null</tt> if the specified key is not mapped.
     * @param key The key whose associated value is to be returned
     * @return The value to which the specified key is mapped,
     * or <tt>null</tt> if the specified key is not mapped.
     */
    public V get(Object key);

    /**
     * Returns true if the current map contains a mapping for the
     * specified key.
     * @param key The key whose presence in the map is being tested.
     * @return true if this map contains a mapping for the specified
     * key.
     */
    public boolean containsKey(Object key);

    /**
     * Returns true if the current map contains no key-value mappings.
     * @return true if the current map contains no key-value mappings.
     */
    public boolean isEmpty();

    /**
     * Removes all of the mappings from the current map.
     */
    public void clear();

    /**
     * Returns the number of key-value mappings in this map
     * @return The number of key-value mappings in this map
     */
    public int size();

    /**
     * Returns the String representation of this map
     * @return The String representation of this map
     */
    public String toString();

}

There are super-cool implementations of these data structures in Fastutils , eg see class Double2FloatAVLTreeMap . Fastutils中有这些数据结构的超酷实现,例如,参见Double2FloatAVLTreeMap类。

The library is open-source, so maybe you would like to study that code (is still interested after all these years). 该库是开源的,所以也许您想研究该代码(这些年来仍然很感兴趣)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM