📜  实现 TreeMap API 的Java程序(1)

📅  最后修改于: 2023-12-03 15:39:08.416000             🧑  作者: Mango

实现 TreeMap API 的 Java 程序

Java 中的 TreeMap 是一种基于红黑树的实现,可以实现从键到值的映射排序,并提供了许多有用的方法。在本文中,我们将介绍如何实现该 API。

红黑树

红黑树是一种自平衡的二叉搜索树。它保证了每个节点最多拥有两个子节点,并且每个节点都有一个颜色,要么红色,要么黑色。根据以下规则,红黑树保证了树的平衡:

  1. 根节点是黑色的
  2. 所有叶子节点都是黑色的空节点(NIL)
  3. 如果一个节点是红色的,则它的两个子节点都是黑色的
  4. 任意节点到其叶子节点的每条路径上都含有相同数目的黑色节点

根据这些规则,红黑树能够在插入和删除节点时自动平衡节点以保持树的平衡。

实现 TreeMap API

我们将通过实现以下 TreeMap API 的方式来使用红黑树:

  • void put(K key, V value)
  • V get(K key)
  • boolean containsKey(K key)
  • boolean containsValue(V value)
  • V remove(K key)
  • int size()
  • boolean isEmpty()
实现
public class TreeMap<K extends Comparable<K>, V> {
    private Node root; // 根结点
    private int size; // 节点数目
    
    // 节点类
    private class Node {
        K key;
        V value;
        Node left, right; // 左右子节点
        int size; // 子树大小
        boolean color; // 节点颜色

        public Node(K key, V value, boolean color) {
            this.key = key;
            this.value = value;
            this.color = color;
        }
    }
    
    // 判断节点颜色
    private boolean isRed(Node node) {
        if (node == null) return false;
        return node.color;
    }

    // 右旋
    private Node rotateRight(Node node) {
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        x.color = node.color;
        node.color = true;
        x.size = node.size;
        node.size = size(node.left) + size(node.right) + 1;
        return x;
    }

    // 左旋
    private Node rotateLeft(Node node) {
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        x.color = node.color;
        node.color = true;
        x.size = node.size;
        node.size = size(node.left) + size(node.right) + 1;
        return x;
    }

    // 转换颜色
    private void flipColors(Node node) {
        node.color = !node.color;
        node.left.color = !node.left.color;
        node.right.color = !node.right.color;
    }

    // 获得节点数目
    private int size(Node node) {
        if (node == null) return 0;
        return node.size;
    }

    // 插入节点 (递归)
    public void put(K key, V value) {
        root = put(root, key, value);
        root.color = false;
    }
    
    // 插入节点 (递归)
    private Node put(Node node, K key, V value) {
        if (node == null) {
            size++;
            return new Node(key, value, true);
        }

        int cmp = key.compareTo(node.key);
        if (cmp < 0) node.left  = put(node.left,  key, value);
        else if (cmp > 0) node.right = put(node.right, key, value);
        else node.value = value;

        if (isRed(node.right) && !isRed(node.left)) node = rotateLeft(node); // 左旋
        if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node); // 右旋
        if (isRed(node.left) && isRed(node.right)) flipColors(node); // 转换颜色

        node.size = size(node.left) + size(node.right) + 1; // 更新子树大小
        return node;
    }

    // 获取节点
    public V get(K key) {
        Node node = root;
        while (node != null) {
            int cmp = key.compareTo(node.key);
            if (cmp < 0) node = node.left;
            else if (cmp > 0) node = node.right;
            else return node.value;
        }
        return null;
    }

    // 是否包含 key
    public boolean containsKey(K key) {
        return get(key) != null;
    }

    // 是否包含 value
    public boolean containsValue(V value) {
        return containsValue(root, value);
    }
    
    // 是否包含 value (递归)
    private boolean containsValue(Node node, V value) {
        if (node == null) return false;
        if (node.value.equals(value)) return true;
        return containsValue(node.left, value) || containsValue(node.right, value);
    }

    // 移除节点
    public V remove(K key) {
        V value = get(key);
        if (value != null) {
            root = remove(root, key);
            size--;
        }
        return value;
    }
    
    // 移除节点 (递归)
    private Node remove(Node node, K key) {
        if (node == null) return null;

        int cmp = key.compareTo(node.key);
        if (cmp < 0) node.left = remove(node.left, key);
        else if (cmp > 0) node.right = remove(node.right, key);
        else {
            if (node.right == null) return node.left;
            if (node.left == null) return node.right;

            Node min = min(node.right);
            min.right = removeMin(node.right);
            min.left = node.left;
            node = min;
        }

        node.size = size(node.left) + size(node.right) + 1; // 更新子树大小

        return node;
    }

    // 获取最小节点
    private Node min(Node node) {
        while (node.left != null) node = node.left;
        return node;
    }

    // 移除最小节点
    private Node removeMin(Node node) {
        if (node.left == null) return node.right;
        node.left = removeMin(node.left);
        node.size = size(node.left) + size(node.right) + 1; // 更新子树大小
        return node;
    }

    // 返回节点数目
    public int size() {
        return size;
    }

    // 是否为空
    public boolean isEmpty() {
        return size == 0;
    }
}

在这个实现中,我们使用了递归方法来执行红黑树的操作。put、get、containsKey、containsValue 和 remove 方法中使用了递归,而 size 和 isEmpty 方法则直接返回类中保存的当前节点数目 size。

结语

通过实现 TreeMap 的 API,我们对红黑树的数据结构有了更深入的了解。这种基于树的数据结构可以非常高效地处理排序数据,是 Java 中许多集合类的底层实现方式。