JAVA实践红黑树-小试牛刀

前言

第一感觉是和AVL树差别不是特别大,没有很直观的感受到效率的巨大提升,作为一个小小的强迫症患者,还是AVL树更好看。

不过讲道理,平衡被破坏次数相同情况下,红黑树的确少了那么一些旋转。。。

因为插入节点后平衡被破坏时,红黑树的会选择旋转或变色。

AVL树则只有旋转。

另外我发现其他的大神写法,跟我的有点差距。。。有心人可以帮我瞄一眼,我是不是哪里错了,在此先谢过了~~

另外一个参考网站、博客、PDF:

红黑树的可视化,插入、删除节点都有动画效果哦~

神奇的讲义,略的比较多,但就胜在简略

一个大神的博客,思路清晰,详细,非常值得参考

实现功能

插入

还有个伪删除。。。可以忽略,加个布尔变量的事。

不然咋叫小试牛刀呢(逃

参考

首先瞄一眼红黑树的性质

再看看长啥样

关于插入的那些事

再次汇总

代码实现

public class RBTree<T extends Comparable<T>> {

    public static void main(String[] args) {

        ///*
        int[] testNum = new int[]{15, 1, 3, 6, 8, 20, 22, 43, 67};
        RBTree<Integer> fuck = new RBTree<Integer>();
        for (int i = 0; i < testNum.length; i++) {
          fuck.insertNode(testNum[i]);
        }
        System.out.println(fuck.root.data);
        //*/
        /*
        RBTree<Character> fuck = new RBTree<Character>();
        fuck.insertNode(‘F‘);
        fuck.insertNode(‘G‘);
        fuck.insertNode(‘D‘);
        fuck.insertNode(‘B‘);
        fuck.insertNode(‘C‘);
        System.out.println(fuck.root.data);
        */
    }

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private TreeNode<T> root;

    /**
     * 插入一个新的节点,插入完毕后符合红黑树性质
     *
     * @param data  需要插入的数据
     */
    public void insertNode(T data) {
        TreeNode<T> curr;
        TreeNode<T> parent = toTargetParent(data);
        if (parent == null) {
            curr = root = new TreeNode<T>(data);
        } else {
            if (data.compareTo(parent.data) < 0) {
                curr = parent.left = new TreeNode<T>(data);
                curr.parent = parent;
            } else {
                curr = parent.right = new TreeNode<T>(data);
                curr.parent = parent;
            }
        }
        fixupTree(curr);
    }

    /**
     * 修复红黑树的不平衡状态
     *
     * @param node  新增节点
     */
    private void fixupTree(TreeNode node) {
        TreeNode parent = null, grandParent = null;
        boolean parentColor = false, uncleColor = false;
        if (node != root) {
            parent = node.parent;
            grandParent = parent.parent;
            parentColor = parent.color;
            uncleColor = getUncleColor(node);
        }
        //父节点为空表示其为根节点
        if (parent == null && node.color == RED) {
            node.color = BLACK;
        } else if (parentColor == RED && uncleColor == RED) {
            changeColor(grandParent);
            //再次判断根节点是否满足要求
            fixupTree(grandParent);
        } else if (parentColor == RED && uncleColor == BLACK) {
            dispatchRotation(grandParent, parent, node);
        }
    }

    /**
     * 判断属于哪种四种情况,LL、LR、RR、RL,使用正确的旋转
     *
     * @param grandParent   祖父节点
     * @param parent    父节点
     * @param child     新增节点
     */
    private void dispatchRotation(TreeNode grandParent, TreeNode parent, TreeNode child) {
        if (grandParent.left == parent) {
            if (parent.left == child) {
                rightRotation(grandParent);
            } else {
                leftRotation(parent);
                rightRotation(grandParent);
            }
        } else {
            if (parent.left == child) {
                rightRotation(parent);
                leftRotation(grandParent);
            } else {
                leftRotation(grandParent);
            }
        }
    }

    /**
     *  改变祖父节点以及两个子节点的颜色
     *
     * @param grandParent   传入新增节点的祖父
     */
    private void changeColor(TreeNode grandParent) {
        grandParent.color = RED;
        if (grandParent.left != null) {
            grandParent.left.color = BLACK;
        }
        if (grandParent.right != null) {
            grandParent.right.color = BLACK;
        }
    }

    /**
     * 返回叔叔节点的颜色
     *
     * @param node 一个节点
     * @return 其叔叔节点的颜色
     */
    private boolean getUncleColor(TreeNode node) {
        TreeNode parent = node.parent;
        //如果当前结点的祖父是空的,说明是其父节点是根节点
        return getBrotherColor(parent.parent == null ? node : parent);
    }

    /**
     * 返回兄弟节点的颜色
     *
     * @param child 传入节点
     * @return 返回兄弟节点的颜色
     */
    private boolean getBrotherColor(TreeNode child) {
        TreeNode parent = child.parent;
        if (parent.left == child && parent.right != null) {
            return parent.right.color;
        } else if (parent.right == child && parent.left != null) {
            return parent.left.color;
        } else {
            return BLACK;
        }
    }

    /**
     *  将传入的节点的右子节点提升为新的父节点,传入节点降为其右子节点
     *  注意颜色、父节点需要处理,务必要清除传入节点的右子节点,因为其已经被提升了父节点了。
     * @param curr  一个节点
     */
    private void leftRotation(TreeNode curr) {
        TreeNode tParent = curr.right;
        tParent.parent = curr.parent;
        tParent.color = BLACK;
        //新父节点的左子节点,放在传入节点的右边
        curr.right = tParent.left;
        if (tParent.left != null) {
            tParent.left.parent = curr;
        }
        //降为子节点前的数据整理
        curr.color = RED;
        curr.parent = tParent;
        tParent.left = curr;
        setChild(curr, tParent);
    }

    /**
     *  将传入的节点的左子节点提升为新的父节点,传入节点降为其右子节点
     *  注意颜色、父节点需要处理,务必要清除传入节点的左子节点,因为其已经被提升了父节点了。
     * @param curr  一个节点
     */
    private void rightRotation(TreeNode curr) {
        //新的父节点
        TreeNode tParent = curr.left;
        tParent.parent = curr.parent;
        tParent.color = BLACK;
        //新父节点的右子节点,放在传入节点的左边
        curr.left = tParent.right;
        if (tParent.right != null) {
            tParent.right.parent = curr;
        }
        //传入节点降为子节点前的数据整理
        curr.color = RED;
        curr.parent = tParent;
        tParent.right = curr;
        setChild(curr, tParent);
    }

    /**
     * 使旋转在树中生效
     *
     * @param roNode    被旋转的节点
     * @param newParent 被旋转之后的父节点
     */
    private void setChild(TreeNode roNode, TreeNode newParent) {
        TreeNode roNodeParent = newParent.parent;
        if (roNodeParent == null) {
            root = newParent;
        } else if (roNodeParent.left == roNode) {
            roNodeParent.left = newParent;
        } else {
            roNodeParent.right = newParent;
        }
    }

    /**
     *  调到data存放位置的父节点处
     * @param data  用于对比的数据
     * @return  data可存放处的父节点
     */
    private TreeNode<T> toTargetParent(T data) {
        TreeNode<T> curr = root;
        TreeNode<T> parent = root;
        while (curr != null) {
            parent = curr;
            if (data.compareTo(curr.data) < 0) {
                curr = curr.left;
            } else {
                curr = curr.right;
            }
        }
        return parent;
    }

    /**
     * 内部节点
     */
    static class TreeNode<T extends Comparable<T>> {
        T data;
        boolean color;
        //伪删除
        boolean isDeleted;
        TreeNode<T> left;
        TreeNode<T> right;
        TreeNode<T> parent;

        TreeNode(T data, boolean color) {
            this.data = data;
            this.color = color;
        }

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

结果

8

经过验证,长这样!

END

时间: 2024-11-05 14:41:09

JAVA实践红黑树-小试牛刀的相关文章

Java实现红黑树

转自:http://www.cnblogs.com/skywang12345/p/3624343.html 红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值.除了具备该特性之外,红黑树还包括许多额外的信息. 红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black).红黑树的特性:(1) 每个节点或者

基于Java实现红黑树的基本操作

首先,在阅读文章之前,我希望读者对二叉树有一定的了解,因为红黑树的本质就是一颗二叉树.所以本篇博客中不在将二叉树的增删查的基本操作了,需要了解的同学可以到我之前写的一篇关于二叉树基本操作的博客:https://www.cnblogs.com/rainple/p/9970760.html: 有随机数节点组成的二叉树的平均高度为logn,所以正常情况下二叉树查找的时间复杂度为O(logn).但是,根据二叉树的特性,在最坏的情况下,比如存储的是一个有序的数据的话,那么所以的数据都会形成一条链,此时二叉

Java数据结构——红黑树

红黑树介绍红黑树(Red-Black Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值.红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black).红黑树的特性: 每个节点或者是黑色,或者是红色. 根节点是黑色. 每个叶子节点是黑色. (注意:这里叶子节点,是指为空的叶子节点) 如果一个节点是红色的,则它的子节点必须是黑色的. 从一个节点到该节点的子孙节点的所有路

java数据结构——红黑树(R-B Tree)

红黑树相比平衡二叉树(AVL)是一种弱平衡树,且具有以下特性: 1.每个节点非红即黑; 2.根节点是黑的; 3.每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的; 4.如图所示,如果一个节点是红的,那么它的两儿子都是黑的; 5.对于任意节点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑节点; 6.每条路径都包含相同的黑节点 原文地址:https://www.cnblogs.com/hardhp74520/p/11317028.html

Java集合详解6:这次,从头到尾带你解读Java中的红黑树

<Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下Star.fork哈 文章首发于我的个人博客: www.how2playlife.com 什么是红黑树 首先,什么是红黑树呢? 红黑树是一种"平衡的"二叉查找树,它是一种经典高效的算法,能够保证

红黑树的理解与Java实现

前言 前段时间在研究 JDK1.8 的 hashmap 源码,看到 put 方法的插入环节,遇到了红黑树,不得不停止阅读源码的过程,因为还没掌握红黑树是无法完全读透 hashmap 源码的.红黑树作为一种数据结构,它被应用得非常多,可能很多人不认识它,但其实它已经在默默为我们的代码在发光发热.例如,你只要在 Java 中用到 map,基本上就是在用红黑树(当元素个数到达八个时链表转红黑树). PS:在看这篇文章前,必须先了解普通的二叉查找树和平衡查找树(AVL)树.2-3-4树.不然看起来会非常

算法导论读书笔记(15) - 红黑树的具体实现

算法导论读书笔记(15) - 红黑树的具体实现 目录 红黑树的简单Java实现 红黑树的简单Java实现 /** * 红黑树 * * 部分代码参考自TreeMap源码 */ public class RedBlackTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; private static

红黑树 实现

转发skywang12345 概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C++/Java实现,原理一样,择其一了解即可. 目录1. 红黑树的介绍2. 红黑树的Java实现(代码说明)3. 红黑树的Java实现(完整源码)4. 红黑树的Java测试程序 转载请注明出处: 更多内容:数据结构与算法系列 目录 (01) 红黑树(一)之 原理和算法详细

红黑树的一个java实现

前几天闲来无事实现了一个红黑树,虽然感觉理解透了,但是真正写码的时候还是调了一个上午才调通,理论还是得联系实践才行啊. 另外可以看看234树,算是红黑树的一个变种,可以加深对红黑树的理解 红黑树性质 1)每个结点要么是红的,要么是黑的. 2)根结点是黑的. 3)每个叶结点,即空结点(NIL)是黑的. 4)如果一个结点是红的,那么它的俩个儿子都是黑的. 5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点. 树的旋转 简单说就是把节点的一个子节点提上来当做主节点,原节点作为子节点