java实现二叉查找树、平衡二叉树、字典

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

java实现二叉查找树

二叉查找树的特点就是,任意节点大于他的左子节点、小于他的右子节点。最优的情况是根节点的左右子树深度相差一,查找效率直逼二分查找。最差的情况下会退化成单向链表,链表的查找效率是很低下的。

实现二叉查找树的关键就是确认加入左子树还是右子树。

public class BST<T> {
    /**
     * 二叉查找树,是特殊的二叉树,任意父节点的值大于其左子节点,小于其右子节点
     */
    class BSTNode {
        //左右子节点
        BSTNode lChild;
        BSTNode rChild;
        //value值
        T value;

        public BSTNode(T value) {
            this.value = value;
        }
    }

    BSTNode root;
    public BST() {
        this.root = new BSTNode(null);
    }

    /**
     * 插入节点
     * @param t
     */
    void insert(T t) {
        Comparable<? super T> tTemp = (Comparable<? super T>) t;
        //可比较的对象
        assert t instanceof Comparable;
        BSTNode rootTemp = root;
        for (; ; ) {
            //头节点未初始化
            if (rootTemp.value == null) {
                rootTemp.value = t;
                return;
            } else if (tTemp.compareTo(rootTemp.value) > 0) {
                if ((rootTemp.rChild) == null) {
                    rootTemp.rChild = new BSTNode(t);
                    return;
                }
                rootTemp = rootTemp.rChild;
            } else {
                if ((rootTemp.lChild) == null) {
                    rootTemp.lChild = new BSTNode(t);
                    return;
                }
                rootTemp = rootTemp.lChild;
            }
        }
    }
    boolean search(T t) {
        Comparable<? super T> tTemp = (Comparable<? super T>) t;
        //可比较的对象
        assert t instanceof Comparable;
        BSTNode rootTemp = root;
        for (; ; ) {
            if (rootTemp == null) {
                return false;
            } else if (tTemp.compareTo(rootTemp.value) == 0) {
                return true;
            } else if (tTemp.compareTo(rootTemp.value) > 0) {
                rootTemp = rootTemp.rChild;
            } else if (tTemp.compareTo(rootTemp.value) < 0) {
                rootTemp = rootTemp.lChild;
            }
        }
    }
    @Test
    public void test() {
        BST<Integer> integerBST = new BST<>();
        integerBST.insert(6);
        integerBST.insert(2);
        integerBST.insert(3);
        integerBST.insert(1);
        integerBST.insert(5);
        integerBST.insert(9);
        integerBST.insert(10);
        integerBST.insert(120);

        System.out.println(integerBST.search(2));
        System.out.println(integerBST.search(10000));
        System.out.println(integerBST.search(10));
        System.out.println(integerBST.search(10));
        System.out.printlnt(integerBST.search(120));
        System.out.println(integerBST.search(5));
    }
}
复制代码

java实现平衡二叉树

平衡二叉树是特殊的二叉查找树,为了防止二叉查找树退化成链表,在插入和删除节点的时候会检测左右子树的深度差小于1,如果深度差大于1就会进行旋转,旋转理论和图示在上一篇说过了,这里就实现。重要的就是旋转的逻辑。

public class AVLTree<T> {

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    class AVLNode {
        //左右子节点
        AVLNode lChild;
        AVLNode rChild;
        //value值
        T value;
        //该节点节对应子树高度
        int height;

        public AVLNode(T value) {
            this.value = value;
        }
    }

    private AVLNode root;

    public AVLTree() {
    }

    /**
     * 插入节点
     * <p>
     * 节点的高度如何确定
     * <p>
     * -左边插入一个
     *
     * @param t
     */
    void insert(T t) {

        root = insert(root, t);
    }

    private AVLNode insert(AVLNode root, T t) {
        //可比较的对象
        assert t instanceof Comparable;
        Comparable<? super T> tTemp = (Comparable<? super T>) t;

        //节点未初始化,递归出口
        if (ObjectUtils.isEmpty(root)) {
            root = new AVLNode(t);
            return root;
        }
        //插入右子树
        else if (tTemp.compareTo(root.value) > 0) {
            root.rChild = insert(root.rChild, t);
            //右右情况
            if (bf(root.rChild) < -1) {
                root = rrRotate(root);

            }
            //右左情况
            else if (bf(root.rChild) > 1) {
                root = rlRotate(root);
            }
        }
        //插入左子树
        else {
            root.lChild = insert(root.lChild, t);
            //左左情况
            if (bf(root.lChild) > 1) {
                root = llRotate(root);
            }
            //左右情况
            else if (bf(root.lChild) < -1) {
                root = lrRotate(root);
            }
        }
        //插入新节点跟新子树高度
        root.height = height(root);
        return root;
    }

    private AVLNode element(AVLNode node, T t) {

        assert t instanceof Comparable;

        Comparable tTemp = (Comparable) t;

        if (tTemp.compareTo(node.value) == 0)
            return node;
        else if (tTemp.compareTo(node.value) > 0)
            return element(node.rChild, t);
        else
            return element(node.lChild, t);
    }

    public AVLNode element(T t) {
        return element(root, t);
    }

    //递归计算节点高度
    private int height(AVLNode node) {
        //当前节点为空,直接返回-1
        if (node == null) return -1;
        //当前节点左右节点为空,返回0
        if (node.lChild == null && node.rChild == null)
            return 0;
        else {
            return Math.max(height(node.lChild), height(node.rChild)) + 1;
        }

    }

    //判断树是否不平衡
    private Boolean isBalance(AVLNode root) {
        return Math.abs(height(root.lChild) - height(root.rChild)) <= 1;
    }

    //小于0,右子树高  大于0 左子树高
    private int bf(AVLNode root) {

        return height(root.lChild) - height(root.rChild);

    }


    //左左情况
    private AVLNode llRotate(AVLNode root) {

        AVLNode lChild = root.lChild;
        //左子树的右节点为,根节点的左节点
        root.lChild = lChild.rChild;
        root.height = height(root);
        //左子树的右节点,为根节点
        lChild.rChild = root;
        //左左情况,只需要跟新  k1  和 k2高度,其他节点的子树叶子节点没有变化
        lChild.height = height(lChild);
        //新的根节点
        return lChild;
    }

    //右右情况
    private AVLNode rrRotate(AVLNode root) {

        AVLNode rChild = root.rChild;
        //左子树的右节点为,根节点的左节点
        root.rChild = rChild.lChild;
        root.height = height(root);
        //左子树的右节点,为根节点
        rChild.lChild = root;
        rChild.height = height(rChild);
        //新的根节点
        return rChild;
    }

    //左右情况
    private AVLNode lrRotate(AVLNode root) {

        //先将左子树进行右旋,并跟新
        root.lChild = rrRotate(root.lChild);
        //再将原树进行左旋
        return llRotate(root);
    }

    //右左情况
    private AVLNode rlRotate(AVLNode root) {
        //先将右子树进行左旋,并跟新
        root.rChild = llRotate(root.rChild);
        //再将原树进行右旋
        return rrRotate(root);
    }


    public static void main(String[] args) {
        AVLTree<Integer> integerAVLTree = new AVLTree<Integer>();

        integerAVLTree.insert(8);
        integerAVLTree.insert(9);
        integerAVLTree.insert(10);
        integerAVLTree.insert(1);
        integerAVLTree.insert(2);
        integerAVLTree.insert(3);
        integerAVLTree.insert(4);
        integerAVLTree.insert(6);
        integerAVLTree.insert(5);
        //验证高度
        System.out.println(integerAVLTree.height(integerAVLTree.root));
        System.out.println(integerAVLTree.root.height);
        //查询节点
        System.out.println(integerAVLTree.element(8));
    }
}
复制代码

java实现字典树

多叉树除了红黑树外,还有一种·trie·树,又叫字典树,专门用于匹配字符串用的。除了根节点,每一个节点都代表一个字符,每一条路径都代表一个字符串,每一个节点都存储着多个子节点引用。

public class TrieTest {

    /**
     * 字典树节点
     * <p>
     * 每个节点有多个子节点,且子节点互不重复
     */
    static class TrieNode {
        //子节点
        HashMap<Character, TrieNode> children = new HashMap<>();
        //字符串结束标志
        Boolean isEnd;
        public TrieNode() {
            this.children = new HashMap<Character, TrieNode>();
            this.isEnd = false;
        }
    }

    //字典树根节点
    private TrieNode root;
    public TrieTest() {
        this.root = new TrieNode();
    }

    //插入字典树
    void insert(String value) {
        TrieNode nodeTemp = root;
        for (int i = 0; i < value.length(); i++) {
            //获取字符
            Character c = value.charAt(i);
            //是否包含此节点
            if (!nodeTemp.children.containsKey(c)) {
                nodeTemp.children.put(c, new TrieNode());
            }
            nodeTemp = nodeTemp.children.get(c);
        }
        //最后一个插入的字符为叶子节点
        nodeTemp.isEnd = true;
    }

    //查找 也是从根节点开始搜索
    boolean search(String value) {

        TrieNode nodeTemp = root;

        for (int i = 0; i < value.length(); i++) {
            //获取字符
            Character c = value.charAt(i);
            //是否包含此节点
            if (!nodeTemp.children.containsKey(c)) {
                return false;
            }
            nodeTemp = nodeTemp.children.get(c);
        }
        return nodeTemp.isEnd;
    }

    @Test
    public void test() {

        TrieTest trieTest = new TrieTest();
        String[] values = new String[]{"a", "to", "tea", "ted", "ten", "i", "in", "ind", "ifd"};

        for (int i = 0; i < values.length; i++) {
            trieTest.insert(values[i]);
        }
        System.out.println("==是否包含ten==>" + trieTest.search("ten"));
        System.out.println("==是否包含xxxx==>" + trieTest.search("xxxx"));
    }

}
复制代码

image-20220517235802704.png

猜你喜欢

转载自juejin.im/post/7105029800347041823