父子结构查询_Java面试准备(5)之数据结构与算法——红黑树

欢迎点赞评论+关注~~~~~~~

e41f9195c377fc98956352e79749fc37.png

如上图,二叉查找树极端情况下可能会变成一个单链表,这种查询时间复杂度就变成O(n)了,红黑树在二叉查找树的基础上进行了自平衡。

1.原理分析

b06bce9b8e8a0c453d485d5a30cdca31.png

如上图,红黑树具有以下特征:

1. 每个节点要么是黑色,要么是红色

2. 根节点是黑色

3. 每个叶子节点都是黑色的空结点(NIL结点)

4. 如果一个节点是红色的,则它的子节点必须是黑色的

5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点

6.新插入节点默认为红色,插入后需要校验红黑树是否符合规则,不符合则需要进行平衡

红黑树节点的增加删除都需要保证满足以上特征,红黑树采取多种方式来维护这些特征,从而维持平衡。

主要包括:左旋转、右旋转、颜色反转

1.1左旋(RotateLeft )

逆时针旋转红黑树的两个结点,使得父结点被自己的右孩子取代,而自己成为自己的左孩子。

77ed5ee821e5781d20f1191f658cb212.png

如图所示,以X为基点逆时针旋转,X的父节点被x原来的右孩子Y取代,c保持不变 ,Y节点原来的左孩子c变成X的右孩子,这样的旋转仍然保证了二叉查找树的特征,左节点比父节点小,右节点仍然比父节点大,即a

1.2 右旋(RotateRight)

顺时针旋转红黑树的两个结点,使得父结点被自己的左孩子取代,而自己成为自己的右孩子。

75f68b6c02ffb487baab5fdbd6400954.png

如图所示,以X为基点顺时针旋转 ,X的父节点被x原来的左孩子Y取代 ,b保持不变 ,Y节点原来的右孩子c变成X的左孩子 。这样的旋转仍然保证了二叉查找树的特征,左节点比父节点小,右节点仍然比父节点大,即b

1.3颜色反转

新增节点默认为红色,而父节点和叔叔节点也为红色,这种情况就违反了红黑树的规则(父与子不能同时为红色),需要将红色往祖辈上传,父节点和叔叔节点变为黑色,这样来保证每条叶子结点到根节点的黑色节点数量并未发生变化。

7d9520684f398c33428141348d1b382d.png

插入新节点的时候存在如下几种情况:

1.插入节点位于树根,没有父节点,这种情况,直接让新结点变色为黑色.

d773ed0b80dc4180636cd1b1ee7da633.png

2.插入节点的父节点是黑色,新插入的红色节点并没有打破红黑树的规则,所以不需要做任何调整

bf7e2428578c5ca56be185e41ad73902.png

3.插入节点的父节点和叔叔节点是红色

325fa197ead9cd6afa96243d58cc52a6.png

此种情况,不满足父子节点不能同时为红色的规则,先让B变成黑色,这样又不满足某一个节点到每个叶子节点经过的黑色节点数相同的规则,先让A变成红色,

9940d86730540c24672c91df18a89267.png

此时,A、C父子节点又变成了红色,所以将C调整为黑色,来使这一局部满足红黑树的规则

239db7ba0ff720492ee0575d4a30db5b.png

4.插入节点的父节点是红色,叔叔节点是黑色或者没有叔叔,且插入节点是父节点的右孩子,父节点是祖父节点点的左孩子,

acf8b4443b21a9850a7749eede37c8da.png

以结点B为轴,做一次左旋转,使得新结点D成为父结点,原来的父结点B成为D的左孩子 ,变成父节点是红色,叔叔节点是黑色或者没有叔叔,新插入节点是父结点的左孩子,父节点是祖父节点的左孩子,以结点A为轴,做一次右旋转,使得结点B成为祖父结点,结点A成为结点B的右孩子,

8145fa68745346b5855263404e95a515.png

接着,将B变成黑色,A变成红色,就局部满足红黑树的规则了。

51f024dcfff3820fd823c66e8fcacec1.png

小结:旋转和颜色反转都是为了是树满足红黑树的5个特性,从而达到自平衡的效果。

变化规律总结:根节点必黑,新增是红色,只能黑连黑,不能红连红; 爸叔通红就变色,爸红叔黑就旋转,哪边黑往哪边转。

2.代码实现

2.1 红黑树节点结构

class RBTreeNode{private int value;//数据    private boolean isBlack;//红黑标记    private RBTreeNode left;//左节点    private RBTreeNode right;//右边节点    private RBTreeNode parent;//父节点    public RBTreeNode(int value) {        this.isBlack=false;//默认节点为红色        this.value = value;    }    setter,getter....}

2.2 遍历节点方法

为了方便获取节点数据,编写一个遍历树的方法,使用递归的方式实现,在树中递归很常用。

public void traverse(RBTreeNode node){        if (node==null) return;        //递归终止条件        if (node.getLeft()==null&& node.getRight()==null){            //叶子节点            System.out.println(node.getValue());            return ;        }        //先遍历左节点,后遍历右节点        traverse(node.getLeft());        traverse(node.getRight());    }

2.3 新增节点

主要是从根节点依次对比 data和node.getValue的大小,一直找到被挂载得节点,在进行挂载。

public void insert(int data){    RBTreeNode node=new RBTreeNode(data);    if (root==null){        //插入根节点        root=node;        root.setBlack(true);//树根是黑色        return;    }    RBTreeNode parent=root;    RBTreeNode son;    if (data

2.4 自平衡方法

自平衡方法按照需求可以拆分为:左旋转、右旋转、设置黑色、设置红色方法

2.4.1 左旋转方法

结合图查看代码逻辑,更清晰旋转过程。

e224d8a4068159a70b31c36f3fc5ab18.png
/**     * 左旋转(逆时针旋转)     * @param node     */    private void leftRotate(RBTreeNode node) {        RBTreeNode right = node.getRight();        RBTreeNode parent = node.getParent();        if (parent==null){            //root节点,将            root=right;            right.setParent(null);        }else{            if (parent.getLeft()!=null&&parent.getLeft()==node){ // 左右                //node是左子节点,左旋转直接将node右子节点旋转挂到父节点左节点                parent.setLeft(right);            }else { //右右的情况                //node是子节点,直接将node的右子节点旋转挂到父节点的右子节点                parent.setRight(right);            }            right.setParent(parent);        }        node.setParent(right);        node.setRight(right.getLeft());        if (right.getLeft()!=null){            right.getLeft().setParent(node);        }        right.setLeft(node);    }

2.4.2 右旋转方法

982f235039b414edffdae10a35120095.png
/**     * 右旋转(顺时针)     * @param node     */    private void rightRotate(RBTreeNode node) {        RBTreeNode left = node.getLeft();        RBTreeNode parent = node.getParent();        if (parent == null) {            //根节点            root = left;            left.setParent(null);        } else {            if (parent.getLeft() != null && parent.getLeft() == node) {                //将node的左子节点挂父节点                parent.setLeft(left);            } else {                //将node的左节点挂父节点的右节点                parent.setRight(left);            }            left.setParent(parent);        }        node.setParent(left);        node.setLeft(left.getRight());        if (left.getRight() != null) {            left.getRight().setParent(node);        } left.setRight(node);    }

2.4.3 设置红黑色方法

private void setRed(RBTreeNode node) {        node.setBlack(false);    }    private void setBlack(RBTreeNode node) {        node.setBlack(true);    }

2.4.4 自平衡方法

/**     * 节点自平衡     * @param node     */    private void nodeBanlance(RBTreeNode node) {        RBTreeNode father,gFather;//父节点,祖父节点        //父节点是红色的        while((father=node.getParent())!=null && father.isBlack()==false){            gFather=father.getParent();            if (gFather.getLeft()==father){                //父节点在祖父节点的左侧                RBTreeNode uncle = gFather.getRight();//叔叔节点                if (uncle!=null&& !uncle.isBlack()){                    //叔叔节点不为空 且是红色的                    //这种情况需要反色,父节点和叔叔节点变黑,爷爷节点变红                    setBlack(father);                    setBlack(uncle);                    setRed(gFather);                    //继续循环                    node=gFather;                    continue;                }                if (node==father.getRight()){                    //需要左旋                    leftRotate(father);                    RBTreeNode tmp=node;                    node=father;                    father=tmp;                }                setBlack(father);                setRed(gFather);                //右旋                rightRotate(gFather);            }//父为祖右孩子            else {                RBTreeNode uncle = gFather.getLeft();                if (uncle != null && !uncle.isBlack()) {                    setBlack(father);                    setBlack(uncle);                    setRed(gFather);                    node = gFather;                    continue;                }                if (node == father.getLeft()) {                    //右旋                    rightRotate(father);                    RBTreeNode tmp = node;                    node = father;                    father = tmp;                }                setBlack(father);                setRed(gFather);                //左旋                leftRotate(gFather);            }        }        setBlack(root);    }

2.5 总结

查询时间复杂度 O(logn)

应用场景:

在JDK1.8中HashMap使用数组+链表+红黑树的数据结构。内部维护着一个数组table,该数组保存着每个链表的表头结点或者树的根节点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中海达数据怎么转rinex_cors账号网最新实战教程,中海达 F61 Plus RTK连接千寻cors账号的方法...

RTK在工程测量工作中越来越流行,在这股潮流中,中海达品牌的F61 Plus因为融合了中海达多年的成熟建站技术HD-CORS,在工程测量工作中也受到了很大欢迎。它可以配套各种cors账号使用,使用过程中只需一台RTK移动站即可进行数据采集、施…

Shiro之UsernamePasswordTokenRememberMeAuthenticationTokenAuthenticationToken

继承关系 先看一下三者的继承关系,会有一个比较清楚的认识 AuthenticationToken AuthenticationToken 用于收集用户提交的身份(如用户名)及凭据(如密码)。Shiro会调用CredentialsMatcher对象的doCredentialsMatch方法对…

华为手机滑动速度设置_华为手机打字速度慢?开启这个设置,一分钟就能打200字...

很多朋友都说自己的打字速度非常慢!每次使用手机聊天、记录工作事项大半天还没打一段字来。其实使用华为手机就能非常快速的码字,不管聊天还是工作,使用这个方法,一分钟就能打200字。1.聊天快速打字手机键盘因为体积的原因&#x…

SpringBoot 使用AOP功能

RPC,AOP都会用到代理,代理的技术有jdk的Proxy代理(必须实现接口),cglib(可以不实现接口,直接实现类),Javassist(jboss )而Spring boot本身也在方方面面使用了代理技术,在Spring中有两种动态代理方式,分别为…

cognos报表导出excel_有了这个报表工具,一键生成自定义的各种报表,还可以导出Excel...

EasyReport是一个简单易用的Web报表工具,它的主要功能是把SQL语句查询出的数据转换成报表页面, 同时支持表格的跨行(RowSpan)与跨列(ColSpan)配置。 同时它还支持报表Excel导出、图表显示及固定表头与左边列的功能。功能介绍本工具从数据库(MySQL,Oracle,SQLServer,…

java自定义注解实现日志功能

一、spring aop的通知类型 1、前置通知(Before):在连接点前执行,不会影响连接点的执行,除非抛异常; 2、后置通知(AfterReturning):在连接点正常执行完成后执行&#xff0…

java自定义注解annotation记录操作日志

说到注解我们平常用的可以说非常多啦,说几个常用的的注解 RestController Service Autowired 这些都是我们平常使用spring框架最常见的注解了,我们只知道它们非常好用,使用RestController 就能构建一个restful的控制器,Service 这个是我们常用…

数组concat_js 标准二维数组变一维数组的方法

问题:[[1, 1], [2, 3], [4, 5]] -> [1, 1, 2, 3, 4, 5]?方法一利用es5的arr.reduce(callback[, initialValue])实现var arr1 [[0, 1], [2, 3], [4, 5]]; var arr2 arr1.reduce(function (a, b) { return a.concat(b)} ); // arr2 [0, 1, 2, 3, 4, 5…

java 自定义注解+AOP实现日志记录

ssm版本: 1、首先自定义一个注解,该注解有两个属性,一个是模块名,一个是操作的内容。该注解是用来修饰Service层中的方法的。 2、创建一个切面类,该切面使用Aspect和Component注解修饰,该页面需要注入一个…

是人是谁_谁是白鹤滩最可爱的人

白鹤滩水电站是全球在建第一大水电站,主要特性指标均位居世界水电工程前列,2021年7月,首批机组投产发电将是白鹤滩工程为建党一百周年献礼的重大壮举。建设中的白鹤滩水电站工程建设不停步,白鹤滩水电站未来的运行管理者——白鹤滩…

potplayer 多个进程_进程组、会话、控制终端概念,如何创建守护进程?

守护进程概念:守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。周期性的执行某种任务或等待处理某些发生的事件。Linux系统有很多守护进程,大多数服务都是用守护进程实现的。比如:像我们的tftp&#xf…

acs880 用户手册_华中数控、广州数控系统用户手册

数控加工仿真系统 广州数控系统用户手册上海宇龙软件工程有限公司2004 年 5 月华中数控、广州数控系统用户手册 目录I目录第一章 基本操作 ............................................................................................ 1 1.1 项目文件 ...................…

亚马逊出的平板电脑_美国最畅销的安卓平板电脑,还只有2GB内存

在美国除了iPad,谁家的平板电脑卖得最好?不是华为小米,也不是微软或谷歌,而是Amazon亚马逊。主打入门级定位的亚马逊Fire系列平板电脑,在北美的平板市场上,有着举足轻重的地位。今天,亚马逊正式…

对接kafka_Kafka系列9:面试题是否有必要深入了解其背后的原理?我觉得应该刨根究底(上)...

​前言在本文开始之前,作者一直有个疑惑,就是面试题是只写写问题和答案就草草了事,还是应该深入分析一下其背后发生的一些原理。和朋友探讨以后作者还是决定采用后者的方式,因为我认为不仅要做到知其一,更要知其二&…

资源不足的情况怎么设置sparkrdd并行度_监控录像机资源不足或达到上限的原因及解决方法!...

在安装网络监控摄像机过程中,很多人遇到硬盘录像机画面上提升“资源不足”或性能“达到上限”的问题,新手遇到这样的问题会选择重启录像机,但是几次反复发现并不能解决。监控录像机资源不足或达到上限的原因及解决方法!首先这种提…

主动变被动9个例句_高中英语 :主动表被动的适用范围

1. 表衡量的动词。The room measures 5 by 6.2. sell, write, wash, read, wear, keep, drink等表状态的情况下。The pen writes well. The clothes wash well.3. 在动词不定式中主语发出的动作。I have a lot of work to do.4. 动词不定式前面的词为形容词时。The question …

为什么要重写hashcode方法和equals方法

为什么要重写hashcode方法和equals方法 我们可能经常听到说重写equals方法必须重写hashcode方法,这是为什么呢?java中所有的类都是Object的子类,直接上object源码 /** Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights …

python代码_python爬虫19 | 遇到需要的登录的网站怎么办?用这3招轻松搞定!

你好由于你是游客无法查看本文请你登录再进谢谢合作当你在爬某些网站的时候需要你登录才可以获取数据咋整?莫慌小帅b把这几招传授给你让你以后从容应对那么接下来就是学习 python 的正确姿势登录的常见方法无非是这两种1、让你输入帐号和密码登录2、让你输入帐号密码…

利用电脑上自带指纹识别嵌入程序中_Win 10 隐藏功能:不用任何软件就可实现手机投屏到电脑...

投屏工具我也介绍了不少,所有的都是需要安装第三方软件的。不过,其实 Windows 10 就自带了手机投影到电脑的功能,不需要任何软件就能实现。在设置中打开系统,找到投影到此电脑,在红框中根据你的需要设置好,…

为什么要重写hashCode()方法和equals()方法以及如何进行重写

一、前言 本篇文章主要探讨的问题有三个: 1、首先我们为什么需要重写hashCode()方法和equals()方法? 2、在什么情况下需要重写hashCode()方法和equals()方法? 3、如何重写这两个方法? 二、为什么需要重写hashCode()方法和equal…