红黑树

<html><head> <meta http-equiv="Content-Type" content="text/html; charset=unicode"> <title>红黑树</title> <style id="wiz_code_style">.wiz-editor-body .wiz-code-container{position: relative; padding:8px 0; margin: 5px 0;text-indent:0; text-align:left;}.CodeMirror {font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; color: black; font-size: 10.5pt; font-size: 0.875rem}.wiz-editor-body .wiz-code-container .CodeMirror div {margin-top: 0; margin-bottom: 0;}.CodeMirror-lines {padding: 4px 0;}.CodeMirror pre {padding: 0 4px;}.CodeMirror pre.CodeMirror-line {min-height: 24px;}.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {background-color: white;}.CodeMirror-gutters {border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap;}.CodeMirror-linenumbers {}.CodeMirror-linenumber {padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap;}.CodeMirror-guttermarker {color: black;}.CodeMirror-guttermarker-subtle {color: #999;}.CodeMirror-cursor {border-left: 1px solid black; border-right: none; width: 0;}.CodeMirror div.CodeMirror-secondarycursor {border-left: 1px solid silver;}.cm-fat-cursor .CodeMirror-cursor {width: auto; border: 0 !important; background: #7e7;}.cm-fat-cursor div.CodeMirror-cursors {z-index: 1;}.cm-fat-cursor-mark {background-color: rgba(20, 255, 20, 0.5);-webkit-animation: blink 1.06s steps(1) infinite;-moz-animation: blink 1.06s steps(1) infinite;animation: blink 1.06s steps(1) infinite;}.cm-animate-fat-cursor {width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7;}@-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {}}@-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {}}@keyframes blink { 0% {} 50% { background-color: transparent; } 100% {}}.CodeMirror-overwrite .CodeMirror-cursor {}.cm-tab { display: inline-block; text-decoration: inherit; }.CodeMirror-rulers {position: absolute; left: 0; right: 0; top: -50px; bottom: -20px; overflow: hidden;}.CodeMirror-ruler {border-left: 1px solid #ccc; top: 0; bottom: 0; position: absolute;}.cm-s-default .cm-header {color: blue;}.cm-s-default .cm-quote {color: #090;}.cm-negative {color: #d44;}.cm-positive {color: #292;}.cm-header, .cm-strong {font-weight: bold;}.cm-em {font-style: italic;}.cm-link {text-decoration: underline;}.cm-strikethrough {text-decoration: line-through;}.cm-s-default .cm-keyword {color: #708;}.cm-s-default .cm-atom {color: #219;}.cm-s-default .cm-number {color: #164;}.cm-s-default .cm-def {color: #00f;}.cm-s-default .cm-variable,.cm-s-default .cm-punctuation,.cm-s-default .cm-property,.cm-s-default .cm-operator {}.cm-s-default .cm-variable-2 {color: #05a;}.cm-s-default .cm-variable-3 {color: #085;}.cm-s-default .cm-comment {color: #a50;}.cm-s-default .cm-string {color: #a11;}.cm-s-default .cm-string-2 {color: #f50;}.cm-s-default .cm-meta {color: #555;}.cm-s-default .cm-qualifier {color: #555;}.cm-s-default .cm-builtin {color: #30a;}.cm-s-default .cm-bracket {color: #997;}.cm-s-default .cm-tag {color: #170;}.cm-s-default .cm-attribute {color: #00c;}.cm-s-default .cm-hr {color: #999;}.cm-s-default .cm-link {color: #00c;}.cm-s-default .cm-error {color: #f00;}.cm-invalidchar {color: #f00;}.CodeMirror-composing { border-bottom: 2px solid; }div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }.CodeMirror-activeline-background {background: #e8f2ff;}.CodeMirror {position: relative; background: #f5f5f5;}.CodeMirror-scroll {overflow: hidden !important; margin-bottom: 0; margin-right: -30px; padding: 16px 30px 16px 0; outline: none; position: relative;}.CodeMirror-sizer {position: relative; border-right: 30px solid transparent;}.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {position: absolute; z-index: 6; display: none;}.CodeMirror-vscrollbar {right: 0; top: 0; overflow-x: hidden; overflow-y: scroll;}.CodeMirror-hscrollbar {bottom: 0; left: 0 !important; overflow-y: hidden; overflow-x: scroll;pointer-events: auto !important;outline: none;}.CodeMirror-scrollbar-filler {right: 0; bottom: 0;}.CodeMirror-gutter-filler {left: 0; bottom: 0;}.CodeMirror-gutters {position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3;}.CodeMirror-gutter {white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px;}.CodeMirror-gutter-wrapper {position: absolute; z-index: 4; background: none !important; border: none !important;}.CodeMirror-gutter-background {position: absolute; top: 0; bottom: 0; z-index: 4;}.CodeMirror-gutter-elt {position: absolute; cursor: default; z-index: 4;}.CodeMirror-gutter-wrapper ::selection { background-color: transparent }.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }.CodeMirror-lines {cursor: text; min-height: 1px;}.CodeMirror pre {-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual;}.CodeMirror-wrap pre {word-wrap: break-word; white-space: pre-wrap; word-break: normal;}.CodeMirror-linebackground {position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0;}.CodeMirror-linewidget {position: relative; z-index: 2; padding: 0.1px;}.CodeMirror-widget {}.CodeMirror-rtl pre { direction: rtl; }.CodeMirror-code {outline: none;}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber {-moz-box-sizing: content-box; box-sizing: content-box;}.CodeMirror-measure {position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;}.CodeMirror-cursor {position: absolute; pointer-events: none;}.CodeMirror-measure pre { position: static; }div.CodeMirror-cursors {visibility: hidden; position: relative; z-index: 3;}div.CodeMirror-dragcursors {visibility: visible;}.CodeMirror-focused div.CodeMirror-cursors {visibility: visible;}.CodeMirror-selected { background: #d9d9d9; }.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }.CodeMirror-crosshair { cursor: crosshair; }.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .CodeMirror div.CodeMirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.CodeMirror-selectedtext { background: none; }.CodeMirror-activeline-background, .CodeMirror-selected {transition: visibility 0ms 100ms;}.CodeMirror-blur .CodeMirror-activeline-background, .CodeMirror-blur .CodeMirror-selected {visibility:hidden;}.CodeMirror-blur .CodeMirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}.CodeMirror-sizer {min-height:auto !important;}</style><style id="wiz_custom_css">html, .wiz-editor-body {font-size: 12pt;}.wiz-editor-body {font-family: Helvetica, 'Hiragino Sans GB', '微软雅黑', 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif;line-height: 1.7;margin: 0 auto;padding: 20px 16px;padding: 1.25rem 1rem;}.wiz-editor-body h1,.wiz-editor-body h2,.wiz-editor-body h3,.wiz-editor-body h4,.wiz-editor-body h5,.wiz-editor-body h6 {margin:20px 0 10px;margin:1.25rem 0 0.625rem;padding: 0;font-weight: bold;}.wiz-editor-body h1 {font-size:20pt;font-size:1.67rem;}.wiz-editor-body h2 {font-size:18pt;font-size:1.5rem;}.wiz-editor-body h3 {font-size:15pt;font-size:1.25rem;}.wiz-editor-body h4 {font-size:14pt;font-size:1.17rem;}.wiz-editor-body h5 {font-size:12pt;font-size:1rem;}.wiz-editor-body h6 {font-size:12pt;font-size:1rem;color: #777777;margin: 1rem 0;}.wiz-editor-body div,.wiz-editor-body p,.wiz-editor-body ul,.wiz-editor-body ol,.wiz-editor-body dl,.wiz-editor-body li {margin:8px 0;}.wiz-editor-body blockquote,.wiz-editor-body table,.wiz-editor-body pre,.wiz-editor-body code {margin:8px 0;}.wiz-editor-body .CodeMirror pre {margin:0;}.wiz-editor-body a {word-wrap: break-word;text-decoration-skip-ink: none;}.wiz-editor-body ul,.wiz-editor-body ol {padding-left:32px;padding-left:2rem;}.wiz-editor-body ol.wiz-list-level1 > li {list-style-type:decimal;}.wiz-editor-body ol.wiz-list-level2 > li {list-style-type:lower-latin;}.wiz-editor-body ol.wiz-list-level3 > li {list-style-type:lower-roman;}.wiz-editor-body li.wiz-list-align-style {list-style-position: inside; margin-left: -1em;}.wiz-editor-body blockquote {padding: 0 12px;}.wiz-editor-body blockquote > :first-child {margin-top:0;}.wiz-editor-body blockquote > :last-child {margin-bottom:0;}.wiz-editor-body img {border:0;max-width:100%;height:auto !important;margin:2px 0;}.wiz-editor-body table {border-collapse:collapse;border:1px solid #bbbbbb;}.wiz-editor-body td,.wiz-editor-body th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-word;box-sizing: border-box;}.wiz-editor-body td > div:first-child {margin-top:0;}.wiz-editor-body td > div:last-child {margin-bottom:0;}.wiz-editor-body img.wiz-svg-image {box-shadow:1px 1px 4px #E8E8E8;}.wiz-hide {display:none !important;}</style></head>
是一个含有红黑结点并能自平衡的二叉查找树;
进行插入和删除等可能会破坏树的平衡的操作时,需要重新自处理达到平衡状态。
查找最坏时间复杂度为O(2lgN),也即整颗树刚好红黑相隔的时候。查找方法和一般平衡二叉树相同;

性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。在Java中,叶子结点是为null的结点。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到该节点的每个叶子结点的路径都包含数量相同的黑结点。确保没有一条路径会比其他路径长出俩倍。
性质5.1:如果一个结点存在黑子结点,那么该结点肯定有两个子结点。

红黑树并不一定是一个完美平衡二叉查找树,根结点P的左子树和右子树之间可能高度不平衡,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到他自己的每个叶子结点的路径都包含数量相同的黑结点(性质5)。所以我们叫红黑树这种平衡为黑色完美平衡
黑结点可以同时包含一个红子结点和一个黑子结点。

红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色。
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了。(可以记忆为该节点高度下降了,以他为圆心逆时针转
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。可以记忆为该节点高度下降了,以他为圆心顺时针转
旋转操作是局部的。
变色:结点的颜色由红变黑或由黑变红。


红黑树插入:
三部分工作:将红黑树当作一颗二叉查找树,将节点插入;将节点着色为红色;通过旋转和重新着色等方法来修正该树,使之重新成为一颗红黑树。

第一步插入很简单,跟查找操作区别不大,就是根据二叉查找树的特性,找到最下面的空节点位置,把目标节点插进去,前提是插入的值目前不在树里的;
第二步,节点着色为"红色",这样不会违背特性5,少违背一条特性,就意味着我们需要处理的情况越少。
第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。接下来分情况叙述。

插入情景1:红黑树为空树
直接把插入结点作为根结点就行,插入结点设为黑色,作为根节点。
插入情景2:插入结点的父结点为黑结点
插入结点设置为红色,不会影响红黑树的平衡,直接插入即可,无需做自平衡。
插入情景3:插入结点的父结点为红结点
该父结点不可能为根结点,所以插入结点总是存在祖父结点。接下来的情况是该情况的子情况。
插入情景3.1:叔叔结点存在并且为红结点
此时祖父结点肯定为黑结点,执行以下操作:
(01) 将“父节点”和 “叔叔节点”设为黑色。
(02) 将“祖父节点”设为“红色”。此时以祖父节点为根节点的子树的黑节点层数没有变化且该子树符合特性;
(03) 将“祖父节点”设为“当前节点”(红色节点);因为祖父节点可能和他的父节点都是红色,就违反了特性;
若此时,祖父节点是根节点,直接将祖父节点设为“黑色”,那就完全解决这个问题了;
若祖父节点不是根节点,那我们需要将“祖父节点”设为“新的当前节点”,接着对“新的当前节点”继续根据情况进行插入分析。
插入情景3.2:叔叔节点是黑色,且当前节点是其父节点的右孩子 
(1)以父节点为支点进行左旋,就变为情景3.3的状况;此时之前的父节点是插入节点的左子节点,我们把之前的父节点作为当前节点,进入情景3.3的处理逻辑;
插入情景3.3:叔叔节点是黑色,且当前节点是其父节点的左孩子
(01) 将当前节点的父节点设为黑色。
(02) 将当前节点的祖父节点设为红色;在设置之前他是黑色。
(03) 以“祖父节点”为支点进行右旋,当前节点的父节点替换祖父节点位置,祖父节点变成父节点的右子树根节点;结束。


红黑树删除:
第一步:将红黑树当作一颗二叉查找树,将节点删除。
(1)被删除节点没有儿子,即为底层节点。那么,直接将该节点删除就OK了。若删除的是红色的则无需进行调整,删除操作结束;若被删节点为黑色的,则在删除后会导致黑高不相等。于是需要对红黑树进行调整,进入第二步。

(2)被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置,子节点设置为黑色。
这里被删节点只有一个儿子,另一个为叶子节点Null,那么由特性5可以知道被删节点唯一的儿子的子树不能有黑节点,所以儿子只有一个节点且为红色,显然可知被删节点必为黑色,所以删除后子红色节点放在被删位置,并且把它设为黑色(为了满足高层的路径黑节点数目);

(3)被删除节点z有两个儿子。那么,先找出它的后继节点y,然后把y的值复制给被删除节点z,节点颜色不复制;之后,我们的目标就是删除后继节点y,使用y的右节点x替代;这里我们不清楚右节点x是不是null,他是可以为叶子节点null的。
当y节点为红色时,移动时红黑树的性质不会被破坏,把右节点x接到y的位置上就结束了;
当y节点为黑色时,一定会出现黑高的不相等,并且也可能会出现两个连续的红色节点。这时需要进行下一步调整,进入第二步。

第二步:调整红黑树结构
若删除的节点z为黑色的,这时,为了保持黑高不被破坏,我们可以将替代y位置的节点x再额外增加一个黑色。
此时节点x可能是双黑或者红黑,若为红黑则只需删除其中的红色保留黑色即可,若为双黑色则需要做进一步的调整。
对于第一步的情况(1),我们知道他没有后继结点,这里我们就把这个要删除节点当作x,然后执行下面的操作;对于第三步如果x是null,则把y当作x执行下面操作,否则删除y用null替代很难理解。

当节点x具有双重黑色的特性时需要对其进行调整。这时有两种情况,分别为:x是父亲的左孩子节点和x是父亲的右孩子节点。由于这两种情况是对称的,因而在此只对x是左孩子节点的情况进行讨论。
当x为左孩子节点时,这时又有四种情况。

1)x的兄弟节点w是红色的。这种情况不好处理,因为x的兄弟节点w为红色,不能将黑色直接上移。所以对其做一些变换,首先将w变成黑色,x的父节点变成红色,然后以x.p为中心进行左旋。旋转后的x的兄弟节点为原来w的左孩子,此节点必为黑色,因为w为红色。通过这一步,我们就能确保x的兄弟节点为黑色,为下一步调整做好准备。

2)x的兄弟节点w是黑色的,并且w的两个子节点也是黑色的。此种情况比较好办,我们可以通过将x和w中的一个黑色上移到x的父节点,使得x的父节点变成新的x节点。在将x上的一个黑色上移后还剩下了一个黑色,将w的黑色上移后w只剩下了红色。若新的x(即原来x的父节点)为红黑色,则将新的x变成黑色,调整结束;若新的x为双黑色时,并且此时的x不为根节点,则继续按这里的分类逻辑进行调整;若x是整棵树的根节点则置为黑色,删除操作结束。

3)x的兄弟节点w是黑色的,并且w的左孩子为红色,右孩子为黑色。这种情况不能提取x和w的黑色上移,因为w的孩子有红色节点。此种情况w的左孩子为红色,右孩子为黑色。我们首先以w为中心进行右旋,并且将w的左孩子的颜色修改为黑色,将w修改为红色。这时将此种情况转化成了第四种情况,即x的兄弟节点w的右孩子为红色节点。

4)x的兄弟节点w是黑色的,并且w的右孩子为红色,左孩子颜色任意。这时,首先将w的颜色改为其父节点的颜色,x的父节点和w的右孩子修改为黑色。然后以x的父节点为中心进行左旋,最后将x指向其根节点,结束。





posted on 2020-03-21 15:22  billxxx  阅读(237)  评论(0编辑  收藏  举报

导航