zoukankan      html  css  js  c++  java
  • 【转载】二叉排序树和平衡二叉树详解

    一、前言

      这两天想学学在Java中广泛使用的数据结构——红黑树,但是看博客说学习红黑树之前需要了解二叉排序树以及平衡二叉树,所以我花了点事件把这两个数据结构学习了一遍,也自己实现了一下这两种数据结构。可以说这是两种比较复杂的数据结构,尤其是平衡二叉树。但幸运的是,我找到了两篇讲解得非常不错的博客,而我这篇博客的目的就是要向想要学习这两种数据结构的人推荐这两篇博客,顺便也是给自己做个标记,方便之后再次阅读。


    二、正文

      首先附上这两篇博客的链接:

      这两篇博客对这两种数据结构讲解的非常的清晰,尤其是第二篇平衡而叉树,感觉真的是完全讲到了点上。为了学习平衡二叉树,我上网搜了很多博客,但是讲解的都不太清楚,尤其是大部分博客所举得例子都不具备普遍性,而上面这篇博客却是一针见血。最最重要的一点是,这两篇博客的代码写的太好了,对于这两种数据结构的实现非常精炼,感觉就是用最简便的方式将它们实现了出来。而且这两篇博客是同一个作者写的,平衡二叉树是二叉搜索树的升级版,而第二篇博客的代码就是在第一篇的基础上修改而来,只要看懂了第一篇的代码,第二篇的也会更容易理解


    三、代码

      上面两篇博客的代码是python写的,我参照着用Java写了一遍,不会python的也可以参考一下Java代码,逻辑是一样的(代码太长建议复制到编译器中再看)。

    (1)二叉排序树

    /**
     * 实现二叉排序树(二叉搜索树)
     */
    public class BinarySearchTree {
    
        private TreeNode root;
    
        /**
         * 树节点
         */
        static class TreeNode {
            private int value;
            private TreeNode lchild;
            private TreeNode rchild;
    
            public TreeNode(int value) {
                this.value = value;
            }
    
            public TreeNode(int value, TreeNode lchild, TreeNode rchild) {
                this.value = value;
                this.lchild = lchild;
                this.rchild = rchild;
            }
        }
    
    
        public void traversal() {
            traversal(root);
            System.out.println();
        }
    
        /**
         * 中序遍历
         *
         * @param root
         */
        private void traversal(TreeNode root) {
            if (root == null)
                return;
            traversal(root.lchild);
            System.out.print(root.value + " ");
            traversal(root.rchild);
        }
    
    
        /**
         * 插入数据
         *
         * @param value
         */
        public void insert(int value) {
            root = insert(root, value);
        }
    
        private TreeNode insert(TreeNode root, int value) {
            if (root == null)
                return new TreeNode(value);
    
            // 若需要插入的节点值小于当前遍历到的节点值,则向做递归
            if (value < root.value)
                root.lchild = insert(root.lchild, value);
            else
                root.rchild = insert(root.rchild, value);
            return root;
        }
    
    
        /**
         * 根据节点值删除节点
         *
         * @param value
         * @return
         */
        public void delete(int value) {
            root = delete(root, value);
        }
    
        /**
         * 删除值为value的节点
         *
         * @param root  当前遍历到的节点
         * @param value 需要删除的节点的value值
         */
        private TreeNode delete(TreeNode root, int value) {
            if (root == null)
                return null;
    
            if (value < root.value)
                root.lchild = delete(root.lchild, value);
            else if (value > root.value)
                root.rchild = delete(root.rchild, value);
            else {
                // 若找到需要删除的节点,则根据节点的子节点数量,判断删除的方法
                // 若当前节点有两个子节点,则将当前节点的左子树中最大的节点覆盖当前节点,
                // 然后将这个子节点删除
                if (root.lchild != null && root.rchild != null) {
                    // 找到当前节点的左子树中,最大的节点
                    TreeNode maxChild = root.lchild;
                    while (maxChild.rchild != null)
                        maxChild = maxChild.rchild;
                    // 删除这个当前节点
                    root = delete(root, maxChild.value);
                    // 将当前节点值改为最大子节点的值
                    root.value = maxChild.value;
                } else {
                    // 若需要删除的节点有0或1个直接子节点,则直接让它的子节点替换它
                    root = (null == root.lchild ? root.rchild : root.lchild);
                }
            }
            return root;
        }
    
    }
    

    (2)平衡二叉树

    /**
     * 平衡二叉树的Java实现
     */
    public class BalancedBinaryTree {
    
        /**
         * 树的根节点
         */
        private TreeNode root;
    
        /**
         * 树节点
         */
        static class TreeNode {
            private int value;
            private int height;
            private TreeNode lchild;
            private TreeNode rchild;
    
            public TreeNode(int value) {
                this.value = value;
            }
    
            public TreeNode(int value, int height) {
                this.value = value;
                this.height = height;
            }
        }
    
    
        /**
         * 中序遍历当前的平衡二叉树
         */
        public void traversal() {
            traversal(root);
            System.out.println();
        }
    
        /**
         * 遍历以当前节点为根节点的平衡二叉树
         * 中序遍历
         * @param root
         */
        private void traversal(TreeNode root) {
            if (null == root)
                return;
    
            traversal(root.lchild);
            System.out.print(root.value + " ");
            traversal(root.rchild);
        }
    
    
        /**
         * 在平衡二叉树中新增加节点
         * @param value
         */
        public void insert(int value) {
            root = insert(root, value);
        }
    
        /**
         * 在以当前节点为根节点的平衡二叉树中新增节点
         * @param root 当前根节点
         * @param value 需要新增节点的value
         */
        private TreeNode insert(TreeNode root, int value) {
            if (null == root)
                return new TreeNode(value);
    
            if (value < root.value)
                root.lchild = insert(root.lchild, value);
            else
                root.rchild = insert(root.rchild, value);
            // 插入完成后判断当前节点的平衡性
            return checkBalance(root);
        }
    
    
        /**
         * 删除节点值为value的节点
         * @param value 需要删除节点的节点值
         * @return
         */
        public void delete(int value) {
            root = delete(root, value);
        }
    
    
        /**
         * 从根节点为root的子树中,删除节点值为value的节点
         * @param root
         * @param value
         * @return
         */
        private TreeNode delete(TreeNode root, int value) {
            if (null == root)
                return null;
    
            if (value < root.value)
                root.lchild = delete(root.lchild, value);
            else if (value > root.value)
                root.rchild = delete(root.rchild, value);
            else {
                /* 找到需要删除的节点后,判断这个节点的子节点的数量
                   子节点数量为0,直接输出;为1,用子节点替代自己
                   若子节点数目为2,分两种情况考虑:
                     1、左子树更深,则使用左子树中做大的节点替代根节点;
                     2、右子树更深,则使用右子树中最小的节点替代根节点;
                 */
                if (null != root.lchild && null != root.rchild) {
                    TreeNode newRoot = getNewRoot(root);
                    root = delete(root, newRoot.value);
                    root.value = newRoot.value;
                } else
                    root = (null == root.lchild ? root.rchild : root.lchild);
            }
            return checkBalance(root);
        }
    
    
        /**
         * 在删除节点的操作中,若需要删除的节点右两个子节点,
         * 则需要找出一个节点直接替代需要深处的节点
         * 可以是左子树中最大的节点,也可以是右子树中最小的节点,根据左右子树的深度判断
         * @param root
         * @return
         */
        private TreeNode getNewRoot(TreeNode root) {
            TreeNode node = null;
            // 左子树更深
            if (root.lchild.height > root.rchild.height) {
                node = root.lchild;
                while (null != node.rchild)
                    node = node.rchild;
            } else {
                node = root.rchild;
                while (null != node.lchild)
                    node = node.lchild;
            }
            return node;
        }
    
    
        /**
         * 判断以当前节点为根节点的子树的平衡性
         * @param root 当前子树的根节点
         * @return 返回根节点
         */
        private TreeNode checkBalance(TreeNode root) {
            if (null == root)
                return null;
    
            // 根据当前节点的左右节点的高度差来判断平衡性
            int diff = getHeightDiff(root);
            if (diff < 2)
                updateHeight(root);
            // 失衡,左深右浅
            else if (diff == 2) {
                int ldiff = getHeightDiff(root.lchild);
                // LL型失衡,直接右旋
                if (ldiff == 1) {
                    root = RotateRight(root);
                // LR型失衡,将右节点左旋,在将当前节点右旋
                } else if(ldiff == -1){
                    root.rchild = RotateLeft(root.rchild);
                    root = RotateLeft(root);
                } else
                    throw new RuntimeException("diff is error");
    
            } else if (diff == -2) {    // 失衡,右深左浅
                int rdiff = getHeightDiff(root.rchild);
                // RR失衡,直接左旋
                if (rdiff == -1)
                    root = RotateLeft(root);
                // RL失衡,先root的右节点右旋,然后root左旋
                else if (rdiff == 1) {
                    root.rchild = RotateRight(root.rchild);
                    root = RotateLeft(root);
                }
    
            } else
                throw new RuntimeException("diff is error");
    
            return root;
        }
    
        /**
         * 获取当前节点的左右子树的高度差
         * @param root
         * @return
         */
        private int getHeightDiff(TreeNode root) {
            // 有两个子节点
            if (null != root.lchild && null != root.rchild)
                return root.lchild.height - root.rchild.height;
            // 只有左子节点
            else if (null != root.lchild)
                return root.lchild.height + 1;
            // 只有右子节点
            else if (null != root.rchild)
                return root.rchild.height + 1;
            // 无子节点
            return 0;
        }
    
        /**
         * 在插入或者删除节点后,更新当前节点的高度
         * @param root
         */
        private void updateHeight(TreeNode root) {
            // 有两个子节点
            if (null != root.lchild && null != root.rchild)
                root.height = Math.max( root.lchild.height, root.rchild.height ) + 1;
                // 只有左子节点
            else if (null != root.lchild)
                root.height = root.lchild.height + 1;
                // 只有右子节点
            else if (null != root.rchild)
                root.height = root.rchild.height + 1;
            // 无子节点
            root.height = 0;
        }
    
    
        /**
         * 将当前节点为根的子树进行右旋
         * @param root
         */
        private TreeNode RotateRight(TreeNode root) {
            TreeNode newRoot = root.lchild;
            root.lchild = newRoot.rchild;
            newRoot.rchild = root;
            updateHeight(root);
            updateHeight(newRoot);
            return newRoot;
        }
    
    
        /**
         * 将当前节点为根的子树进行左旋
         * @param root
         * @return
         */
        private TreeNode RotateLeft(TreeNode root) {
            TreeNode newRoot = root.rchild;
            root.rchild = newRoot.lchild;
            newRoot.lchild = root;
            updateHeight(root);
            updateHeight(newRoot);
            return newRoot;
        }
    
    }
    
  • 相关阅读:
    P2590 [ZJOI2008]树的统计(树链剖分)
    【算法】线性排序
    【LeetCode每天一题】Median of Two Sorted Arrays(两数组中的中位数)
    【算法】归并排序
    【LeetCode每天一题】Longest Substring Without Repeating Characters(最长无重复的字串)
    【算法】快排
    【LeetCode每天一题】Add Two Numbers(两链表相加)
    【LeetCode每天一题】Two Sum(两数之和)
    【算法】选择排序
    【算法】插入排序
  • 原文地址:https://www.cnblogs.com/tuyang1129/p/12536314.html
Copyright © 2011-2022 走看看