分类 数据结构 下的文章

哈希表是一种类似于 TreeNode 数组的数据结构。添加、查找极快,缺点是牺牲了顺序性。

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

    private final int[] capacity
            = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
            49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
            12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};

    private static final int upperTol = 10;
    private static final int lowerTol = 2;
    private int capacityIndex = 0;

    private TreeMap<K, V>[] hashtable;
    private int size;
    private int M;

    public HashTable() {
        this.M = capacity[capacityIndex];
        size = 0;
        hashtable = new TreeMap[M];
        for (int i = 0; i < M; i++) {
            hashtable[i] = new TreeMap<>();
        }
    }

    private int hash(K key) {
        return (key.hashCode() & 0x7fffffff) % M;
    }

    public int getSize() {
        return size;
    }

    public void add(K key, V value) {
        TreeMap<K, V> map = hashtable[hash(key)];
        if (map.containsKey(key))
            map.put(key, value);
        else {
            map.put(key, value);
            size++;

            if (size >= upperTol * M && capacityIndex + 1 < capacity.length) {
                capacityIndex++;
                resize(capacity[capacityIndex]);
            }
        }
    }

    public V remove(K key) {
        V ret = null;
        TreeMap<K, V> map = hashtable[hash(key)];
        if (map.containsKey(key)) {
            ret = map.remove(key);
            size--;

            if (size < lowerTol * M && capacityIndex - 1 >= 0) {
                capacityIndex--;
                resize(capacity[capacityIndex]);
            }
        }
        return ret;
    }

    public void set(K key, V value) {
        TreeMap<K, V> map = hashtable[hash(key)];
        if (!map.containsKey(key)) {
            throw new IllegalArgumentException(key + " doesn't exist!");
        }
        map.put(key, value);
    }

    public boolean contains(K key) {
        return hashtable[hash(key)].containsKey(key);
    }

    public V get(K key) {
        return hashtable[hash(key)].get(key);
    }

    private void resize(int newM) {
        TreeMap<K, V>[] newHashTable = new TreeMap[newM];
        for (int i = 0; i < newM; i++) {
            newHashTable[i] = new TreeMap<>();
        }

        int oldM = M;
        this.M = newM;
        for (int i = 0; i < oldM; i++) {
            TreeMap<K, V> map = hashtable[i];
            for (K key : map.keySet()) {
                newHashTable[hash(key)].put(key, map.get(key));
            }
        }

        this.hashtable = newHashTable;
    }
}

红黑树是一种相对平衡的二叉查找树,要求任何一条路径的长度不超过其他路径长度的2倍。AVL是高度平衡的二叉查找树,要求每个节点的左右子树高度差不超过1。

AVL树的查找效率更高,但维持平衡的成本也更高。在需要频繁查找时,选用AVL树更合适,在需要频繁插入、删除时,选用红黑树更合适。

import java.util.LinkedList;
import java.util.Queue;
public class RedBlackTree {

    TreeNode root;
    static final boolean RED = true;
    static final boolean BLACK = false;

    //查找结点
    public TreeNode search(int data) {
        TreeNode tmp = root;
        while (tmp != null) {
            if (tmp.data == data) {
                return tmp;
            } else if (tmp.data > data) {
                tmp = tmp.left;
            } else {
                tmp = tmp.right;
            }
        }
        return null;
    }

    //插入结点
    public boolean insert(int data) {
        TreeNode node = new TreeNode(data);
        //局面1:新结点位于树根,没有父结点。
        if (root == null) {
            root = node;
            node.color = BLACK;
            return true;
        }
        TreeNode targetNode = root;
        while (targetNode != null) {
            if (data == targetNode.data) {
                System.out.println("红黑树中已有重复的结点:" + data);
                return false;
            } else if (data > targetNode.data) {
                if (targetNode.right == null) {
                    targetNode.right = node;
                    node.parent = targetNode;
                    insertAdjust(node);
                    return true;
                }
                targetNode = targetNode.right;
            } else {
                if (targetNode.left == null) {
                    targetNode.left = node;
                    node.parent = targetNode;
                    insertAdjust(node);
                    return true;
                }
                targetNode = targetNode.left;
            }
        }
        return true;
    }

    //插入后自我调整
    private void insertAdjust(TreeNode node) {
        //创建父结点和祖父结点指针
        TreeNode parent, grandParent;
        //局面3的调整有可能引发后续的一系列调整,所以使用while循环。
        while (node.parent != null && node.parent.color == RED) {
            parent = node.parent;
            grandParent = parent.parent;
            if (grandParent.left == parent) {
                TreeNode uncle = grandParent.right;
                //局面3:新结点的父结点和叔叔结点都是红色。
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    uncle.color = BLACK;
                    grandParent.color = RED;
                    node = grandParent;
                    continue;
                }
                //局面4:新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右孩子,父结点是祖父结点的左孩子。
                if (node == parent.right) {
                    leftRotate(parent);
                    TreeNode tmp = node;
                    node = parent;
                    parent = tmp;
                }
                //局面5:新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的左孩子,父结点是祖父结点的左孩子。
                parent.color = BLACK;
                grandParent.color = RED;
                rightRotate(grandParent);
            } else {
                TreeNode uncle = grandParent.left;
                //局面3(镜像):新结点的父结点和叔叔结点都是红色。
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    uncle.color = BLACK;
                    grandParent.color = RED;
                    node = grandParent;
                    continue;
                }
                //局面4(镜像):新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的左孩子,父结点是祖父结点的右孩子。
                if (node == parent.left) {
                    rightRotate(parent);
                    TreeNode tmp = node;
                    node = parent;
                    parent = tmp;
                }
                //局面5(镜像):新结点的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右孩子,父结点是祖父结点的右孩子。
                parent.color = BLACK;
                grandParent.color = RED;
                leftRotate(grandParent);
            }
        }
        //经过局面3的调整,有可能把根结点变为红色,此时再变回黑色即可。
        if (root.color == RED) {
            root.color = BLACK;
        }
    }

    //删除节点
    public void remove(int key) {
        remove(search(key));
    }

    //删除节点详细逻辑
    private void remove(TreeNode node) {
        TreeNode targetNode = node;
        if (node == null) {
            return;
        }
        //第一步:如果待删除结点有两个非空的孩子结点,转化成待删除结点只有一个孩子(或没有孩子)的情况。
        if (node.left != null && node.right != null) {
            //待删除结点的后继结点
            TreeNode successNode = targetNode.right;
            while (successNode.left != null) {
                successNode = successNode.left;
            }
            if (targetNode == root) {
                root = successNode;
            }
            //把后继结点复制到待删除结点位置
            targetNode.data = successNode.data;
            remove(successNode);
            return;
        }
        //第二步:根据待删除结点和其唯一子结点的颜色,分情况处理。
        TreeNode successNode = node.left != null ? node.left : node.right;
        TreeNode parent = node.parent;
        if (parent == null) {
            //子情况1,被删除结点是红黑树的根结点:
            root = successNode;
            if (successNode != null) {
                successNode.parent = null;
            }
        } else {
            if (successNode != null) {
                successNode.parent = parent;
            }
            if (parent.left == node) {
                parent.left = successNode;
            } else {
                parent.right = successNode;
            }
        }
        if (node.color == BLACK) {
            //第三步:遇到双黑结点,在子结点顶替父结点之后,分成6种子情况处理。
            removeAdjust(parent, successNode);
        }

    }

    //删除结点后的自我调整
    private void removeAdjust(TreeNode parent, TreeNode node) {
        while ((node == null || node.color == BLACK) && node != root) {
            if (parent.left == node) {
                //node的兄弟节点
                TreeNode sibling = parent.right;
                //子情况3,node的兄弟结点是红色:
                if (sibling != null && sibling.color == RED) {
                    parent.color = RED;
                    sibling.color = BLACK;
                    leftRotate(parent);
                    sibling = parent.right;
                }
                if (sibling == null || ((sibling.left == null || sibling.left.color == BLACK) && (sibling.right == null || sibling.right.color == BLACK))) {
                    //子情况2(镜像),node的父结点是黑色,兄弟和侄子结点是黑色:
                    if (parent.color == BLACK) {
                        sibling.color = RED;
                        node = parent;
                        parent = node.parent;
                        continue;
                    }
                    //子情况4(镜像),node的父结点是红色,兄弟和侄子结点是黑色:
                    else {
                        sibling.color = RED;
                        break;
                    }
                }
                //子情况5,node的父结点随意,兄弟结点是黑色右孩子,左侄子结点是红色,右侄子结点是黑色:
                if (sibling.left == null || sibling.color == RED) {
                    sibling.left.color = BLACK;
                    sibling.color = RED;
                    rightRotate(sibling);
                    sibling = sibling.parent;
                }
                //子情况6,结点2的父结点随意,兄弟结点B是黑色右孩子,右侄子结点是红色:
                sibling.color = parent.color;
                parent.color = BLACK;
                sibling.right.color = BLACK;
                leftRotate(parent);
                node = root; //跳出循环
            } else {
                //node的兄弟节点
                TreeNode sibling = parent.left;
                //子情况3(镜像),node的兄弟结点是红色:
                if (sibling != null && sibling.color == RED) {
                    parent.color = RED;
                    sibling.color = BLACK;
                    rightRotate(parent);
                    sibling = parent.left;
                }
                if (sibling == null || ((sibling.left == null || sibling.left.color == BLACK) && (sibling.right == null || sibling.right.color == BLACK))) {
                    //子情况2(镜像),node的父结点是黑色,兄弟和侄子结点是黑色:
                    if (parent.color == BLACK) {
                        sibling.color = RED;
                        node = parent;
                        parent = node.parent;
                        continue;
                    }
                    //子情况4(镜像),node的父结点是红色,兄弟和侄子结点是黑色:
                    else {
                        sibling.color = RED;
                        break;
                    }
                }
                //子情况5(镜像),node的父结点随意,兄弟结点是黑色左孩子,右侄子结点是红色,左侄子结点是黑色:
                if (sibling.right == null || sibling.right.color == RED) {
                    sibling.right.color = BLACK;
                    sibling.color = RED;
                    leftRotate(sibling);
                    sibling = sibling.parent;
                }
                //子情况6(镜像),结点2的父结点随意,兄弟结点是黑色左孩子,左侄子结点是红色:
                sibling.color = parent.color;
                parent.color = BLACK;
                sibling.left.color = BLACK;
                rightRotate(parent);
                node = root; //跳出循环
            }
        }
        if (node != null) {
            node.color = BLACK;
        }
    }

    //左旋转
    private void leftRotate(TreeNode node) {
        TreeNode right = node.right;
        TreeNode parent = node.parent;
        if (parent == null) {
            root = right;
            right.parent = null;
        } else {
            if (parent.left != null && parent.left == node) {
                parent.left = right;
            } else {
                parent.right = right;
            }
            right.parent = parent;
        }
        node.parent = right;
        node.right = right.left;
        if (right.left != null) {
            right.left.parent = node;
        }
        right.left = node;
    }

    //右旋转
    private void rightRotate(TreeNode node) {
        TreeNode left = node.left;
        TreeNode parent = node.parent;
        if (parent == null) {
            root = left;
            left.parent = null;
        } else {
            if (parent.left != null && parent.left == node) {
                parent.left = left;
            } else {
                parent.right = left;
            }
            left.parent = parent;
        }
        node.parent = left;
        node.left = left.right;
        if (left.right != null) {
            left.right.parent = node;
        }
        left.right = node;
    }

    //中序遍历
    public static void inOrderTraversal(TreeNode node) {
        if (node != null) {
            inOrderTraversal(node.left);
            System.out.print(node);
            inOrderTraversal(node.right);
        }
    }

    //层序遍历
    public static void levelOrderTraversal(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            System.out.print(node);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

    class TreeNode {
        int data;
        boolean color;
        TreeNode left;
        TreeNode right;
        TreeNode parent;

        public TreeNode(int data) {
            this.data = data;
            this.color = RED;
        }

        @Override
        public String toString() {
            return data + (color ? "(red)" : "(black)") + "  ";
        }
    }

    public static void main(String[] args) {
        RedBlackTree rbTree = new RedBlackTree();
        int input[] = {13, 8, 17, 1, 11, 15, 25, 6, 22, 27};
        for (int i = 0; i < input.length; i++) {
            rbTree.insert(input[i]);
        }
        rbTree.remove(8);
        System.out.println("中序遍历: ");
        inOrderTraversal(rbTree.root);
        System.out.println();
        System.out.println("层序遍历: ");
        levelOrderTraversal(rbTree.root);
        System.out.println();
    }
}

平衡二叉树,是一种特殊的二叉查找树,也被称为AVL树。AVL是历史上第一棵自平衡二叉树,是高度平衡的二叉查找树,要求每个节点的左右子树高度差不超过1。

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

    private class Node {
        public K key;
        public V value;
        public Node left, right;
        public int height;

        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;
    }

    private void inOrder(Node node, ArrayList<K> keys) {
        if (node == null) {
            return;
        }

        inOrder(node.left, keys);
        keys.add(node.key);
        inOrder(node.right, keys);
    }

    /**
     * 判断该二叉树是否是一棵二分搜索树
     *
     * @return 该二叉树是否是一棵二分搜索树
     */
    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;
    }


    /**
     * 判断该二叉树是否是一棵平衡二叉树
     *
     * @return 该二叉树是否是一棵平衡二叉树
     */
    public boolean isBalanced() {
        return isBalanced(root);
    }

    /**
     * 判断以Node为根的二叉树是否是一棵平衡二叉树,递归算法
     *
     * @param node 根节点
     * @return 以Node为根的二叉树是否是一棵平衡二叉树
     */
    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);
    }

    /**
     * 获得节点node的高度
     *
     * @param node 节点
     * @return 高度
     */
    private int getHeight(Node node) {
        if (node == null) {
            return 0;
        }
        return node.height;
    }

    /**
     * 获得节点node的平衡因子
     *
     * @param node 节点
     * @return 平衡因子
     */
    private int getBalanceFactor(Node node) {
        if (node == null) {
            return 0;
        }
        return getHeight(node.left) - getHeight(node.right);
    }

    // 对节点y进行向右旋转操作,返回旋转后新的根节点x
    //        y                              x
    //       / \                           /   \
    //      x   T4     向右旋转 (y)        z     y
    //     / \       - - - - - - - ->    / \   / \
    //    z   T3                       T1  T2 T3 T4
    //   / \
    // T1   T2
    private Node rightRotate(Node y) {
        Node x = y.left;
        Node T3 = x.right;

        // 向右旋转过程
        x.right = y;
        y.left = T3;

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

        return x;
    }

    // 对节点y进行向左旋转操作,返回旋转后新的根节点x
    //    y                             x
    //  /  \                          /   \
    // T1   x      向左旋转 (y)       y     z
    //     / \   - - - - - - - ->   / \   / \
    //   T2  z                     T1 T2 T3 T4
    //      / \
    //     T3 T4
    private Node leftRotate(Node y) {
        Node x = y.right;
        Node T2 = x.left;

        // 向左旋转过程
        x.left = y;
        y.right = T2;

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

        return x;
    }

    /**
     * 向二分搜索树中添加新的元素(key, value)
     *
     * @param key
     * @param value
     */
    public void add(K key, V value) {
        root = add(root, key, value);
    }

    // 向以node为根的二分搜索树中插入元素(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 {
            // key.compareTo(node.key) == 0
            node.value = value;
        }

        // 更新height
        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;
    }

    /**
     * 返回以node为根节点的二分搜索树中,key所在的节点
     *
     * @param node 根节点
     * @param key
     * @return key所在节点
     */
    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 contains(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;
    }

    /**
     * 返回以node为根的二分搜索树的最小值所在的节点
     *
     * @param node 根节点
     * @return 以node为根的二分搜索树的最小值所在的节点
     */
    private Node minimum(Node node) {
        if (node.left == null) {
            return node;
        }
        return minimum(node.left);
    }

    /**
     * 从二分搜索树中删除键为key的节点
     *
     * @param key
     * @return 要删除节点的值
     */
    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 {   // key.compareTo(node.key) == 0
            // 待删除节点左子树为空的情况
            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;
        }

        // 更新height
        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;
    }

}

并查集这种数据结构,用于处理联通问题。

public interface UF {

    /**
     * 获取元素个数
     *
     * @return 元素个数
     */
    int getSize();

    /**
     * 查看元素p和元素q是否所属一个集合
     *
     * @param p 元素
     * @param q 元素
     * @return 元素p和元素q是否所属一个集合
     */
    boolean isConnected(int p, int q);

    /**
     * 合并元素p和元素q所属的集合
     *
     * @param p 元素
     * @param q 元素
     */
    void unionElements(int p, int q);
}
public class UnionFind implements UF {

    // rank[i]表示以i为根的集合所表示的树的层数
    // 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
    // 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
    private int[] rank;
    private int[] parent; // parent[i]表示第i个元素所指向的父节点

    /**
     * 构造函数
     *
     * @param size 元素个数
     */
    public UnionFind(int size) {
        rank = new int[size];
        parent = new int[size];

        // 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
        for (int i = 0; i < size; i++) {
            parent[i] = i;
            rank[i] = 1;
        }
    }

    @Override
    public int getSize() {
        return parent.length;
    }

    /**
     * 查找过程, 查找元素p所对应的集合编号
     * O(h)复杂度, h为树的高度
     *
     * @param p 元素
     * @return 元素p所对应的集合编号
     */
    private int find(int p) {
        if (p < 0 || p >= parent.length) {
            throw new IllegalArgumentException("p is out of bound.");
        }

        // path compression 2, 递归算法
        if (p != parent[p]) {
            parent[p] = find(parent[p]);
        }
        return parent[p];
    }

    /**
     * 查看元素p和元素q是否所属一个集合
     * O(h)复杂度, h为树的高度
     *
     * @param p 元素
     * @param q 元素
     * @return 元素p和元素q是否所属一个集合
     */
    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /**
     * 合并元素p和元素q所属的集合
     * O(h)复杂度, h为树的高度
     *
     * @param p 元素
     * @param q 元素
     */
    @Override
    public void unionElements(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot) {
            return;
        }

        // 根据两个元素所在树的rank不同判断合并方向
        // 将rank低的集合合并到rank高的集合上
        if (rank[pRoot] < rank[qRoot]) {
            parent[pRoot] = qRoot;
        } else if (rank[qRoot] < rank[pRoot]) {
            parent[qRoot] = pRoot;
        } else {
            // rank[pRoot] == rank[qRoot]
            parent[pRoot] = qRoot;
            // 此时, 我维护rank的值
            rank[qRoot] += 1;
        }
    }
}

字典树一般用于处理字符串。

public class Trie {

    private class Node {
        /**
         * 是否是单词
         */
        public boolean isWord;
        /**
         * 下一个节点map
         */
        public TreeMap<Character, Node> next;

        public Node(boolean isWord) {
            this.isWord = isWord;
            next = new TreeMap<>();
        }

        public Node() {
            this(false);
        }
    }

    /**
     * 根节点
     */
    private Node root;
    /**
     * 单词数
     */
    private int size;

    /**
     * 无参构造函数
     */
    public Trie() {
        root = new Node();
        size = 0;
    }

    /**
     * 获得Trie中存储的单词数量
     *
     * @return Trie中存储的单词数量
     */
    public int getSize() {
        return size;
    }

    /**
     * 向Trie中添加一个新的单词word
     *
     * @param word 要添加的单词
     */
    public void add(String word) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (cur.next.get(c) == null) {
                cur.next.put(c, new Node());
            }
            cur = cur.next.get(c);
        }

        if (!cur.isWord) {
            cur.isWord = true;
            size++;
        }
    }

    /**
     * 查询单词word是否在Trie中
     *
     * @param word 要查询的单词
     * @return 是否包含
     */
    public boolean contains(String word) {
        Node cur = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (cur.next.get(c) == null) {
                return false;
            }
            cur = cur.next.get(c);
        }
        return cur.isWord;
    }

    /**
     * 查询是否在Trie中有单词以prefix为前缀
     *
     * @param prefix 前缀
     * @return 是否包含
     */
    public boolean isPrefix(String prefix) {
        Node cur = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix.charAt(i);
            if (cur.next.get(c) == null) {
                return false;
            }
            cur = cur.next.get(c);
        }
        return true;
    }

    /**
     * 删除word, 返回是否删除成功
     *
     * @param word 要删除的单词
     * @return 是否删除成功
     */
    public boolean remove(String word) {
        // 将搜索沿路的节点放入栈中
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        for (int i = 0; i < word.length(); i++) {
            if (!stack.peek().next.containsKey(word.charAt(i))) {
                return false;
            }
            stack.push(stack.peek().next.get(word.charAt(i)));
        }

        if (!stack.peek().isWord) {
            return false;
        }

        // 将该单词结尾isWord置空
        stack.peek().isWord = false;
        size--;

        // 如果单词最后一个字母的节点的next非空,
        // 说明trie中还存储了其他以该单词为前缀的单词,直接返回
        if (stack.peek().next.size() > 0) {
            return true;
        } else {
            stack.pop();
        }

        // 自底向上删除
        for (int i = word.length() - 1; i >= 0; i--) {
            stack.peek().next.remove(word.charAt(i));
            // 如果一个节点的isWord为true,或者是其他单词的前缀,则直接返回
            if (stack.peek().isWord || stack.peek().next.size() > 0) {
                return true;
            }
            stack.pop();
        }
        return true;
    }

}