首先手撸红黑树,需要明白红黑树是什么东西?
FBI警告:
千万不要去看源码去理解红黑树!
千万不要去看源码去理解红黑树!
千万不要去看源码去理解红黑树!(重要的事情说三遍,因为你会懵逼的,除非你是大神!!!)
其实红黑树其底层就是(特殊的,只是类似自平衡)二叉查找树.就是这么简单
很多人懵逼就是不知道其底层结构.
网上很多教程提到的情况
可能有同学会问,什么是二叉查找树?什么是平衡树?对于这两个有疑问的同学,可以看下我往期的博客
30分钟看完数据结构和算法原理
首先要明白 红黑树五条性质:
性质一:节点是红色或者是黑色;
在树里面的节点不是红色的就是黑色的,没有其他颜色,要不怎么叫红黑树呢,是吧。
性质二:根节点是黑色;
根节点总是黑色的。它不能为红。
性质三:每个叶节点(NIL或空节点)是黑色;
这个图片就是一个红黑树,NIL节点是个空节点,并且是黑色的。
性质四:每个红色节点的两个子节点都是黑色的(也就是说不存在两个连续的红色节点,黑色的可以连在一起);
就是连续的两个节点不能是连续的红色,连续的两个节点的意思就是父节点与子节点不能是连续的红色。
性质五:从任一节点到其没个叶节点的所有路径都包含相同数目的黑色节点;
下面的图从根节点到每一个NIL节点的路径中,都包含了相同数量的黑色节点。
这五条性质约束了红黑树,可以通过数学证明来证明,满足这五条性质的二叉树可以将查找删除维持在对数时间内。
当我们进行插入或者删除操作时所作的一切操作都是为了调整树使之符合这五条性质。
下面我们先介绍两个基本操作,旋转。
旋转的目的是将节点多的一支出让节点给另一个节点少的一支,旋转操作在插入和删除操作中经常会用到,所以要熟记。
什么是左旋?左旋就是将当前要旋转的点进行逆时钟转90度,然后把要旋转的点(未旋转之前)的右节点的左子树节点挂到要旋转的点(已经旋转完毕)的右子树节点
好懵逼啊!!!不着急用一个动画了解
左旋图:
右旋恰好相反
右旋图:
接下来讲插入:
首先一个节点要插进来,颜色默认红色,根据节点的大小,想按照二叉查找树的规则插到相应的位置.
然后就要进行颜色变换了
变换规则如下
(下面这段话非常重要,一定要看完)
首先我们要明白有几种情况需要注意一下(新插入的节点默认为红色!)
1.如果是插入到根节点,直接改为黑色即可
2.如果插入的的父节点是黑色,直接插入即可
3.出现双红,也就是如果插入的节点的父节点是红色.通俗讲就是有两个红色的节点连在一起了.
这里面分两种情况.
(1).如果插入的节点的父节点的兄弟(叔叔节点)如果都是红色,就直接改颜色. 把父节点和叔叔节点改为黑色,把爷爷节点改为红色.
(2)如果插入的节点的父节点的兄弟(叔叔节点)如果是黑色,下面要分两种情况
A.如果插入的节点的父节点是爷爷节点左子树
如果当前节点作为父节点的右子树,则进行左旋
怎么左旋:左旋之前,当前指针移动到当前节点的父节点
如果当前节点作为父节点的左子树,则进行右旋
怎么右旋:右旋之前,当前指针移动到当前节点的爷爷节点,然后把父节点改为黑色,爷爷节点改为红色
B.如果插入的节点的父节点是爷爷节点右子树(这点很重要,网上的教程几乎没讲,自己摸索出来的)
如果当前节点作为父节点的右子树,则进行左旋(这里跟上面情况一样)
怎么左旋:左旋之前,当前指针移动到当前节点的爷爷节点,然后把父节点改为黑色,爷爷节点改为红色
如果当前节点作为父节点的左子树,则进行右旋(这里跟上面情况一样)
怎么右旋:右旋之前,当前指针移动到当前节点的父节点
又是好懵逼啊?!!!!
别着急用一个例子分析
首先我已经有了一个红黑树,现在有一个节点6想往里面插(我承认我没有开车),先根据二叉查找树的规则插到相应的位置,如下图
然后插完你会发现6 和7 颜色一样,连在一起了,破坏了红黑树的规则了(有毒.......)
出现这种问题,只能进行接下来的颜色变换了(根据3.1规则)
首先当前节点6 的父亲7变为黑色
再把当前节点6 的叔叔13变为黑色
再把当前节点6 的爷爷12变为红色
然后再把指针移到12这个节点(也就是当前节点变成12了)
如下图:
然后又发现,5和12节点颜色一样了(可怕...........)
接下来我们想进行上一步的做法,却发现,当前节点(也就是12)的叔叔(也就是30)是黑色,(尴尬......) (根据3.2规则)
继续先判断,12节点的父节点作为爷爷节点的左子树(根据3.2.A规则),很明显5节点作为19的左子树
接下来可能要进行左旋或者是右旋了,
如果当前节点作为父节点的右子树,则进行左旋
如果当前节点作为父节点的左子树,则进行右旋
很明显,12这个节点为5节点的右子树,需要进行左旋.左旋之前,当前指针移动到当前节点的父节点(也就是5节点变为当前节点)
左旋后的图:
接下来又发现,5和12节点颜色一样了,然后5节点的叔叔是黑色,
继续先判断,5节点的父节点作为爷爷节点的左子树(根据3.2.A规则),很明显12节点作为19的左子树
要左旋或者右旋,但是5节点作为12的左子树,需要右旋
右旋需要注意:
1.先把5的父亲12节点变为黑色,再把5的爷爷变为红色
2.指针移到爷爷,也就是当前节点变为19节点了
3.以当前节点19进行右旋
右旋后的图:
红黑树插入完成
大家可以去下面的网址去练一下,红黑树的生成过程
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
创建红黑树代码
//红黑树
//节点
class Node {
constructor(value) {
this.value = value
this.left = null
this.right = null
this.parent = null
this.color = "r" //默认红色
}
}
//树
class Tree {
constructor() {
this.root = null
// this.findLeaf()
}
//添加节点
add(data) {
let node = new Node(data)
//如果根节点为空
if (this.root == null) {
node.color = "b"
this.root = node
return
}
let leaft = this.findLeaf(this.root, data)//获取插入在哪个叶子节点上
this.addLR(leaft, node)//插入到叶子节点
this.changeToSuccess(node)//调用纠正
// this.root = this.findRoot(this.root)//寻找根节点,因为多次旋转之后,一开始插入的节点早就不是根节点了
}
//查找要插入的叶子节点
findLeaf(root, data) {
if (root == null) {
return null
}
if (data < root.value) {
if (root.left == null) {
return root
}
return this.findLeaf(root.left, data) //这里的return可能有点绕, 最后相当把 return root 这个结果给返回
} else {
if (root.right == null) {
return root
}
return this.findLeaf(root.right, data)
}
}
//插在叶子节点的左边还是右边
addLR(leaft, node) {
if (leaft == null || node == null) {
retutn
}
node.parent = leaft
if (node.value < leaft.value) {
//leaft.left = node
this.addL(leaft, node)
return
}
if (node.value > leaft.value) {
//leaft.right = node
this.addR(leaft, node)
return
}
}
//插在节点的左边
addL(leaft, node) {
if (node != null) {
node.parent = leaft
}
if (leaft != null) {
leaft.left = node
}
}
//插在节点的右边
addR(leaft, node) {
if (node != null) {
node.parent = leaft
}
if (leaft != null) {
leaft.right = node
}
}
/**
* 寻找根节点,现在用不到了
* @param {*} node
*/
findRoot(node) {
if (node.parent == null) {
return node
} else {
return this.findRoot(node.parent) //这里一定要return ,相当于最后把 return node 返回
}
// return node.parent == null ? node : this.findRoot(node.parent)
}
/**
* 修正红黑树函数
* @param {*} node
*/
changeToSuccess(node) {
if (node == null) return
//如果插入的第一个节点是root节点,直接改颜色
if (node == this.root) {
this.root.color = "b"
return
}
//如果插入的节点是黑色,不用改颜色
if (this.getNodeColor(node.parent) == "b") {
return
}
//如果插入的父亲和叔叔是红色
if (this.getNodeColor(node.parent.parent.left) == "r" &&
this.getNodeColor(node.parent.parent.right) == "r") {
node.parent.parent.left.color = "b"
node.parent.parent.right.color = "b"
node.parent.parent.color = "r"
this.changeToSuccess(node.parent.parent)//当前节点变为爷爷,这里要递归一下,因为有可能爷爷跟上面的节点出现双红
return
}
//自己作为父亲的左子树 ,父亲作为爷爷右子树(右旋)
if (node == node.parent.left && node.parent == node.parent.parent.right) {
this.rotateR(node.parent) //node.parent这个是当前的指针
this.changeToSuccess(node.right)//寻找旋转之前node.parent的位置(因为是当前指针的位置)
return
}
//自己作为父亲的左子树 ,父亲作为爷爷左子树(右旋+改颜色)
if (node == node.parent.left && node.parent == node.parent.parent.left) {
node.parent.color = "b" //先改颜色
node.parent.parent.color = "r"
this.rotateR(node.parent.parent)
this.changeToSuccess(node.parent.right)
return
}
//自己作为父亲的右子树 ,父亲作为爷爷左子树(左旋)
if (node == node.parent.right && node.parent == node.parent.parent.left) {
this.rotateL(node.parent) //node.parent这个是当前的指针
this.changeToSuccess(node.left)//寻找旋转之前node.parent的位置
return
}
//自己作为父亲的右子树 ,父亲作为爷爷右子树(左旋+改颜色)
if (node == node.parent.right && node.parent == node.parent.parent.right) {
node.parent.color = "b" //先改颜色
node.parent.parent.color = "r"
this.rotateL(node.parent.parent)
this.changeToSuccess(node.parent.left)
return
}
}
/**
* 获取节点的颜色
* @param {*} node
*/
getNodeColor(node) {
return node == null ? "b" : node.color
}
//左旋
rotateL(node) {
let parent = node.parent
let self = node
let right = node.right
let son = right.left
if (parent != null) { // node节点父亲为空,说明node为root节点
this.addLR(parent, right)// node节点父亲操作
this.addR(node, son) //node本身节点操作
this.addLR(right, self) //node右子树操作
} else {
this.addR(node, son) //node本身节点操作
this.addLR(right, self) //node右子树操作
right.parent = null //这一步非常重要,如果不清空,后面想找父节点很难找
this.root=right //这个根节点要记录
}
}
//右旋
rotateR() {
let parent = node.parent
let self = node
let left = node.left
let son = left.right
if (parent != null) { // node节点父亲为空,说明node为root节点
this.addLR(parent, left)// node节点父亲操作
this.addR(node, son) //node本身节点操作
this.addLR(left, self) //node左子树操作
} else {
this.addR(node, son) //node本身节点操作
this.addLR(left, self) //node左子树操作
left.parent = null //这一步非常重要,如果不清空,后面想找父节点很难找
this.root=left //这个根节点要记录
}
}
/**
*
* 树遍历,采用前序遍历
*/
printTree(){
if(this.root!=null){
console.log("采用前序遍历")
this.prePrint(this.root)
}
}
//前序遍历
prePrint(root){
console.log("值:"+root.value,"颜色:"+root.color)
if(root.left){
this.prePrint(root.left)
}
if(root.right){
this.prePrint(root.right)
}
}
}
// let tree = new Tree()
//测试
function test() {
let tree = new Tree()
let i = 0
//插入5个数据
while (i++ < 5) {
tree.add(i)
}
//打印树
tree.printTree()
}
test()
继续完善红黑树功能
1.删除功能(比较麻烦)
2.修改功能(前面实现,这个就简单)
3.查询功能(简单)
4.添加功能(前面已经实现)
代码如下:
//红黑树
//节点
class Node {
constructor(value) {
this.value = value
this.left = null
this.right = null
this.parent = null
this.color = "r" //默认红色
}
}
//树
class Tree {
constructor() {
this.root = null
// this.findLeaf()
}
//添加节点
add(data) {
let node = new Node(data)
//如果根节点为空
if (this.root == null) {
node.color = "b"
this.root = node
return
}
let leaft = this.findLeaf(this.root, data)//获取插入在哪个叶子节点上
this.addLR(leaft, node)//插入到叶子节点
this.changeToSuccess(node)//调用纠正
// this.root = this.findRoot(this.root)//寻找根节点,因为多次旋转之后,一开始插入的节点早就不是根节点了
}
//查找要插入的叶子节点
findLeaf(root, data) {
if (root == null) {
return null
}
if (data < root.value) {
if (root.left == null) {
return root
}
return this.findLeaf(root.left, data) //这里的return可能有点绕, 最后相当把 return root 这个结果给返回
} else {
if (root.right == null) {
return root
}
return this.findLeaf(root.right, data)
}
}
//插在叶子节点的左边还是右边
addLR(leaft, node) {
if (leaft == null || node == null) {
retutn
}
node.parent = leaft
if (node.value < leaft.value) {
//leaft.left = node
this.addL(leaft, node)
return
}
if (node.value > leaft.value) {
//leaft.right = node
this.addR(leaft, node)
return
}
}
//插在节点的左边
addL(leaft, node) {
if (node != null) {
node.parent = leaft
}
if (leaft != null) {
leaft.left = node
}
}
//插在节点的右边
addR(leaft, node) {
if (node != null) {
node.parent = leaft
}
if (leaft != null) {
leaft.right = node
}
}
/**
* 寻找根节点,现在用不到了
* @param {*} node
*/
findRoot(node) {
if (node.parent == null) {
return node
} else {
return this.findRoot(node.parent) //这里一定要return ,相当于最后把 return node 返回
}
// return node.parent == null ? node : this.findRoot(node.parent)
}
/**
* 修正红黑树函数
* @param {*} node
*/
changeToSuccess(node) {
if (node == null) return
//如果插入的第一个节点是root节点,直接改颜色
if (node == this.root) {
this.root.color = "b"
return
}
//如果插入的节点是黑色,不用改颜色
if (this.getNodeColor(node.parent) == "b") {
return
}
//如果插入的父亲和叔叔是红色
if (this.getNodeColor(node.parent.parent.left) == "r" &&
this.getNodeColor(node.parent.parent.right) == "r") {
node.parent.parent.left.color = "b"
node.parent.parent.right.color = "b"
node.parent.parent.color = "r"
this.changeToSuccess(node.parent.parent)//当前节点变为爷爷,这里要递归一下,因为有可能爷爷跟上面的节点出现双红
return
}
//自己作为父亲的左子树 ,父亲作为爷爷右子树(右旋)
if (node == node.parent.left && node.parent == node.parent.parent.right) {
this.rotateR(node.parent) //node.parent这个是当前的指针
this.changeToSuccess(node.right)//寻找旋转之前node.parent的位置(因为是当前指针的位置)
return
}
//自己作为父亲的左子树 ,父亲作为爷爷左子树(右旋+改颜色)
if (node == node.parent.left && node.parent == node.parent.parent.left) {
node.parent.color = "b" //先改颜色
node.parent.parent.color = "r"
this.rotateR(node.parent.parent)
this.changeToSuccess(node.parent.right)
return
}
//自己作为父亲的右子树 ,父亲作为爷爷左子树(左旋)
if (node == node.parent.right && node.parent == node.parent.parent.left) {
this.rotateL(node.parent) //node.parent这个是当前的指针
this.changeToSuccess(node.left)//寻找旋转之前node.parent的位置
return
}
//自己作为父亲的右子树 ,父亲作为爷爷右子树(左旋+改颜色)
if (node == node.parent.right && node.parent == node.parent.parent.right) {
node.parent.color = "b" //先改颜色
node.parent.parent.color = "r"
this.rotateL(node.parent.parent)
this.changeToSuccess(node.parent.left)
return
}
}
/**
* 获取节点的颜色
* @param {*} node
*/
getNodeColor(node) {
return node == null ? "b" : node.color
}
//左旋
rotateL(node) {
let parent = node.parent
let self = node
let right = node.right
let son = right.left
if (parent != null) { // node节点父亲为空,说明node为root节点
this.addLR(parent, right)// node节点父亲操作
this.addR(node, son) //node本身节点操作
this.addLR(right, self) //node右子树操作
} else {
this.addR(node, son) //node本身节点操作
this.addLR(right, self) //node右子树操作
right.parent = null //这一步非常重要,如果不清空,后面想找父节点很难找
this.root = right //这个根节点要记录
}
}
//右旋
rotateR() {
let parent = node.parent
let self = node
let left = node.left
let son = left.right
if (parent != null) { // node节点父亲为空,说明node为root节点
this.addLR(parent, left)// node节点父亲操作
this.addR(node, son) //node本身节点操作
this.addLR(left, self) //node左子树操作
} else {
this.addR(node, son) //node本身节点操作
this.addLR(left, self) //node左子树操作
left.parent = null //这一步非常重要,如果不清空,后面想找父节点很难找
this.root = left //这个根节点要记录
}
}
/**
*
* 树遍历,采用前序遍历
*/
printTree() {
if (this.root != null) {
console.log("采用前序遍历")
this.prePrint(this.root)
}
}
//前序遍历
prePrint(root) {
console.log("值:" + root.value, "颜色:" + root.color)
if (root.left) {
this.prePrint(root.left)
}
if (root.right) {
this.prePrint(root.right)
}
}
//寻找目标值的节点,高效
findNode(root, data) {
if (data == root.value) {
return root
}
if (data < root.value) { //如果目标值小于当前节点值,往左边走
if (root.left != null) {
return this.findNode(root.left, data) //最后相当于 return root
}
}
if (data > root.value) { //如果目标值大于当前节点值,往右边走
if (root.right != null) {
return this.findNode(root.right, data)
}
}
}
//全部前序遍历一次,低效(这里不建议用)
findNode2(root, data) {
let node = null
if (data == root.value) {
return root
}
if (root.left != null) {
node = this.findNode(root.left, data) //最后相当于 return root
if (node != null) {
return node //找到一定要return,不然后面继续运行,导致node又被清空
}
}
if (root.right != null) {
node = this.findNode(root.right, data)
if (node != null) {
return node
}
}
}
//判断当前节点是父亲的左子树,并清空储存父节点的值,从而达到删除的目的
clearNode(node) {
if (node.parent.left == node) {
node.parent.left = null
} else {
node.parent.right = null
}
}
//删除节点
deleteNode(data, n = null) {
//这个n=null 非常有用,后面递归的时候,不用再去根据值去找那个节点了
if (this.root != null) {
let node
if (n == null) {
node = this.findNode(this.root, data)
} else {
node = n
}
//如果是叶子节点直接删除
if (node.left == null && node.right == null) {
//删除操作 (红黑树删除分几种情况)
//1.当被删除元素为红时,直接删除(这里的节点都是叶子节点了)
if (node.color == "r") {
this.clearNode(node)
return
}
//2.当被删除元素为黑且为根节点时,直接删除
if (node.color == "b" && node.parent == null) {
this.clearNode(node)
return
}
//4.当被删除元素为黑,
//且兄弟节点为黑,兄弟节点两个孩子也为黑,父节点为红,此时,
//交换兄弟节点与父节点的颜色
if (node.parent.left == node) { //判断是左兄弟还是右兄弟
if (node.color == "b" && this.getNodeColor(node.parent) == "r"
&& this.getNodeColor(node.parent.right) == "b"
&& this.getNodeColor(node.parent.right.left) == "b"
&& this.getNodeColor(node.parent.right.right) == "b") {
let bcolor = node.parent.right.color
node.parent.right.color = this.getNodeColor(node.parent)
node.parent.color = bcolor
this.clearNode(node)
return
}
} else {
if (node.color == "b" && this.getNodeColor(node.parent) == "r"
&& this.getNodeColor(node.parent.left) == "b"
&& this.getNodeColor(node.parent.left.left) == "b"
&& this.getNodeColor(node.parent.left.right) == "b"
) {
let bcolor = node.parent.left.color
node.parent.left.color = this.getNodeColor(node.parent)
node.parent.color = bcolor
this.clearNode(node)
return
}
}
//5.当被删除元素为黑、并且为父节点的左支,且兄弟颜色为黑,兄弟的右支为红色,(1,3,5,8)
//交换兄弟与父亲的颜色,并把兄弟的右支涂黑,并以父节点为中心左转
if (node.color == "b" && node.parent.left == node && this.getNodeColor(node.parent.right) == "b"
&& this.getNodeColor(node.parent.right.right) == "r") {
let bcolor = this.getNodeColor(node.parent.right)
node.parent.right.color = this.getNodeColor(node.parent)
node.parent.color = bcolor
node.parent.right.right.color = "b"
this.rotateL(node.parent)
this.clearNode(node)
return
}
//5.1当被删除元素为黑、并且为父节点的右支,且兄弟颜色为黑,兄弟的右支为红色,(1,2,4,5)
//兄弟与兄弟的右子节点颜色互换,以兄弟节点为中心进行左转,然后就变成了规则6.1一样了,在按照规则6.1进行旋转
if (node.color == "b" && node.parent.right == node &&
this.getNodeColor(node.parent.left) == "b"
&& this.getNodeColor(node.parent.left.right) == "r") {
let bcolor = this.getNodeColor(node.parent.left)
node.parent.left.color = this.getNodeColor(node.parent.left.right.color)
node.parent.left.right.color = bcolor
this.rotateL(node.parent.left)
this.deleteNode(node.value, node)
return
}
//6.当被删除元素为黑、并且为父节点的左支,且兄弟颜色为黑,兄弟的左支为红色,
//兄弟与兄弟的左子节点颜色互换,以兄弟节点为中心进行右转,然后就变成了规则5一样了,在按照规则5进行旋转
if (node.color == "b" && node.parent.left == node && this.getNodeColor(node.parent.right) == "b"
&& this.getNodeColor(node.parent.right.left) == "r"
) {
let bcolor = this.getNodeColor(node.parent.right)
node.parent.right.color = this.getNodeColor(node.parent.left)
node.parent.left.color = bcolor
this.rotateR(node.parent.right)
this.deleteNode(node.value, node)
return
}
//6.1当被删除元素为黑、并且为父节点的右支,且兄弟颜色为黑,兄弟的左支为红色,
//交换兄弟与父亲的颜色,并把兄弟的右支涂黑,并以父节点为中心右转(1,4,5,8)
if (node.color == "b" && node.parent.right == node && this.getNodeColor(node.parent.left) == "b"
&& this.getNodeColor(node.parent.left.left) == "r"
) {
let bcolor = this.getNodeColor(node.parent.left)
node.parent.left.color = this.getNodeColor(node.parent)
parent.color = bcolor
node.parent.left.right.color = "b"
this.rotateR(node.parent)
this.clearNode(node)
return
}
//8.被删除元素为黑且兄弟节点为黑,兄弟节点的孩子为黑,父亲为黑,
//这个时候需要将兄弟节点变为红,再把父亲看做那个被删除的元素(只是看做,实际上不删除),看看父亲符和哪一条删除规则
if (node.parent.left == node) { //判断是左兄弟还是右兄弟
if (node.color == "b" && this.getNodeColor(node.parent) == "b"
&& this.getNodeColor(node.parent.right) == "b"
&& this.getNodeColor(node.parent.right.left) == "b"
&& this.getNodeColor(node.parent.right.right) == "b") {
this.deleteNode(node.parent.value, node.parent)
return
}
} else {
if (node.color == "b" && this.getNodeColor(node.parent) == "b"
&& this.getNodeColor(node.parent.left) == "b"
&& this.getNodeColor(node.parent.left.left) == "b"
&& this.getNodeColor(node.parent.left.right) == "b") {
this.deleteNode(node.parent.value, node.parent)
return
}
}
//9.当被删除的元素为黑,且为父元素的左支,兄弟节点为红色
//交换兄弟节点与父亲结点的颜色,以父亲结点进行左旋,就变成了情况4,
if (node.color == "b" && node.parent.left == node
&& this.getNodeColor(node.parent.right) == "r") {
let bcolor = this.getNodeColor(node.parent.right)
node.parent.right.color = this.getNodeColor(node.parent)
node.parent.color = bcolor
this.rotateL(node.parent)
this.deleteNode(node.value, node)
return
}
}
//如果被删除的元素有一个子节点,可以将子节点直接移到被删除元素的位置(红黑树只换值)
if (node.left == null || node.right == null) {
if (node.left != null) { //有左子树
let val = node.left.value
node.left.value = node.value
node.value = val
this.deleteNode(node.left.value, node.left)
return
}
if (node.right != null) { //有右子树
let val = node.right.value
node.right.value = node.value
node.value = val
//3.当被删除元素为黑,且有一个右子节点为红时,将右子节点涂黑放到被删除元素的位置
if (node.color == "b") {
if (node.right.color == "r") {
node.color = "b"
node.right.color = "r"
}
}
this.deleteNode(node.right.value, node.right)
return
}
}
//如果有两个子节点,这时候就可以把被删除元素的右支的最小节点互换,(红黑树只换值)
if (node.left != null && node.right != null) {
let rsNode = this.findRightSmall(node.right)
let val = node.value
node.value = rsNode.value
rsNode.value = val
this.deleteNode(rsNode.value, rsNode)
return
}
}
}
//寻找右子树最左的节点
findRightSmall(node) {
if (node.left == null) {
return node
}
return this.findRightSmall(node.left)
}
//修改数据,先删除,后添加
editNode(data, val) {
this.deleteNode(data)
this.add(val)
}
//查找节点
selectNode(data) {
if (this.root != null) {
return this.findNode(this.root, data)
}
}
}
// let tree = new Tree()
//测试
function test() {
let tree = new Tree()
let i = 0
//插入5个数据
while (i++ < 8) {
tree.add(i)//添加节点
}
// tree.deleteNode(7)//删除7的节点
// tree.editNode(1,60)//把1修改成60
let node = tree.selectNode(5)//查询5的节点
tree.printTree()//打印
console.log(node)
}
test()