10.红黑树

红黑树是实际应用中最常用的平衡二叉查找树,它不严格的具有平衡属性。(平衡属性:任意节点的左右子树的高度相差不大于1),平均的使用性能却很良好。

一、什么是红黑树。

  • 结点被标记为红色和黑色两种颜色
  • 根节点是黑色的,每个叶结点都是不存储数据的黑色空结点。
  • 任何相邻的节点都不能同时为红色
  • 任意节点到其可到达的叶结点间包含相同数量的黑色结点。

这样保证了:

  • 没有一条路径比其他路径长出2倍
  • 树的高度稳定趋近于 log ⁡ 2 n \log_{2}{n} log2n,从而各种操作的时间复杂度为 log ⁡ 2 n \log_{2}{n} log2n
  • 一棵有n个结点的红黑树的高度,最多是 2 log ⁡ 2 ( n + 1 ) 2\log_{2}{(n+1)} 2log2(n+1)

二、各种操作的具体实现

1.变色和旋转

(1)变色:颜色由红变黑或者由黑变红。

(2)左旋:结点的右孩子成为结点的父亲;原来结点右孩子的左子树变为,结点的右子树;结点的父结点,变成右子树的父节点

(3)右旋:与左旋对称,结点的左孩子成为结点的父结点,结点的做孩子的右子树指向结点自己,结点的父结点,变成做孩子的父结点。

注意:

(1)旋转操作不会改变树中序遍历的顺序

(2)旋转操作通过降级高度高的子树的高度,增加高度低的子树的高度来维护二叉树的平衡。

代码实现

package tree

import "fmt"

const ColorRed = 0
const ColorBlack = 1

type RBTree struct {
	Root *RBNode
}

type RBNode struct {
	Left, Right, Parent *RBNode
	Color               int
	Data                int
}

func (n *RBNode) LeftRotate(t *RBTree) {
	if n == nil {
		return
	}

	right := n.Right

	n.Right = right.Left
	if right.Left != nil {
		n.Right.Parent = n
	}
	right.Parent = n.Parent
	if n.Parent == nil {
		t.Root = right
	} else if n.Parent.Left == n {
		n.Parent.Left = right
	} else {
		n.Parent.Right = right
	}
	right.Left = n
	n.Parent = right
}

func (x *RBNode) RightRotate(t *RBTree) {
	if x == nil {
		return
	}

	y := x.Left
	x.Left = y.Right
	if y.Right != nil {
		y.Right.Right = x
	}
	y.Parent = x.Parent
	if x.Parent == nil {
		t.Root = y
	} else if x == x.Parent.Left {
		x.Parent.Left = y
	} else {
		x.Parent.Right = y
	}
	y.Right = x
	x.Parent = y
}

func (x *RBNode) ChangeColor() {
	if x.Color == ColorRed {
		x.Color = ColorBlack
	} else {
		x.Color = ColorRed
	}
}
func (x *RBNode) GetColor() int {
	if x == nil {
		return ColorBlack
	} else {
		return x.Color
	}
}
2.插入操作

结点之间的关系:

(1)父结点

(2)祖父结点:父结点的父结点

(3)叔叔结点

插入过程:

(1)插入:同二叉搜索树插入,只是每次插入结点的颜色都是红色。

(2)修复(父结点为红色事才需修复):

​ a. z是根节点:直接上色为黑色

​ b. z的叔结点是红色的: 对z的祖父结点,祖父结点,和叔结点变色。

​ c. z的叔结点是黑色的,并且z局部呈现直线: 旋转z的祖父结点,直线朝右则向左旋转,直线朝左,则向右旋转;原来的父亲变黑和祖父变红,变成情况b,按照b处理;

​ d. z的叔结点是黑色的,并且z局部呈现三角形:旋转z的父结点,使z变成直线;类似情况c 进行操作。

func (t *RBTree) SearchAddNode(ele int) *RBNode {
	node := RBNode{Data: ele}
	traval := t.Root
	if traval == nil {
		t.Root = &node
		return &node
	}
	// 1.平衡二叉树插入
	for i := 0; i < 100; i++ {
		if ele < traval.Data {
			if traval.Left == nil {
				traval.Left = &node
				node.Parent = traval
				break
			} else {
				traval = traval.Left
			}
		} else {
			if traval.Right == nil {
				traval.Right = &node
				node.Parent = traval
				break
			} else {
				traval = traval.Right
			}
		}
	}
	return &node
}

func (t *RBTree) AddNode(ele int) {
	node := t.SearchAddNode(ele)

	// 父元素为红色需要修复
	for node.Parent.GetColor() == ColorRed {
		// 获取叔结点
		var uncle *RBNode
		if node.Parent.Parent.Left == node.Parent {
			uncle = node.Parent.Parent.Right
		} else {
			uncle = node.Parent.Parent.Left
		}
		// 1. z的叔结点是红色的
		if uncle.GetColor() == ColorRed {
			uncle.ChangeColor()
			node.Parent.ChangeColor()
			node.Parent.Parent.ChangeColor()
			node = node.Parent.Parent
			continue
		} else {
			// 2. z的叔结点是黑色的,并且z局部呈现直线:
			if node.Parent.Left == node && node.Parent.Parent.Left == node.Parent {
				p := node.Parent
				gp := node.Parent.Parent
				gp.RightRotate(t)
				p.ChangeColor()
				gp.ChangeColor()
				node = node.Parent.Parent
				continue
			}
			if node.Parent.Right == node && node.Parent.Parent.Right == node.Parent {
				p := node.Parent
				gp := node.Parent.Parent
				gp.LeftRotate(t)
				p.ChangeColor()
				gp.ChangeColor()
				node = node.Parent.Parent
				continue
			}
			// 3. z的叔结点是黑色的,并且z局部呈现三角形
			if node.Parent.Right == node && node.Parent.Parent.Left == node.Parent {
				p := node.Parent
				node.Parent.LeftRotate(t)
				node = p
				continue
			}
			if node.Parent.Left == node && node.Parent.Parent.Right == node.Parent {
				p := node.Parent
				node.Parent.RightRotate(t)
				node = p
				continue
			}
		}

		return
	}
	t.Root.Color = ColorBlack
}
3.删除操作

这是红黑树,最后一节,也是最复杂,最重要的一节。

要保证:

  1. 二叉搜索树的性质成立

    1. 红黑树的性质成立。

所以红黑树的删除,分为2部分:(1)类似于二叉树搜索树的删除 (2) 红黑树的修复。

删除操作如下:

(1)结点删除:

​ a. 没有子结点:直接删除

​ b.有一个子节点:删除结点,并把自己结点移动到,自己的位置

​ c.有两个子树,把结点和结点的后继结点的值交换,删除后继结点。

后继结点:右子树的最右侧结点,后继结点是中序遍历中下一个结点,c的做法,不破坏搜索树的性质

(2)结点修复:(上面中,c会被转化成 a 或者 b 所以,结点只会有,一个子结点,或者没有子节点)

情况 1: 删除的是红色结点(

红色结点只能是叶子结点(红色结点下,只能是两个黑色结点或者没有结点),直接删除。

情况2: 删除的是黑色结点

​ 我们设置 删除结点后替代原结点的结点设为 x ,x的兄弟结点 为 w,我们分以下4中情况讨论(下面加上x再左,w在右侧,相反的情况对称处理)

a. w是黑色,且其子结点都是黑色
              A
            /   \
       -1 x(黑)  w(黑)
          	   /   \
          	 C(黑)  B(黑)

由于 x端子树的黑高减小了1,我们需要把 w端的黑高统一减1,步骤如下:

  • w变成红色
  • 把x的父结点作为x继续进行修复操作
           -1 x -- 新w
            /   \
         A(黑)  w(红)
          	   /   \
          	 C(黑)  B(黑)
b. w是黑色,且其右子结点,为红色.
              A
            /   \
       -1 x(黑)  w(黑)
          	   /   \
          	 C(黑)  B(红)
          	 

通过以下步骤,就能维护红黑树的性质(不用再转化成其他情况):

  1. w.color = x.p.color
  2. x.p.color = black
    w.right.color = black
  3. left-rotate(t,x.p)
           w
         /   \  
      A(黑)  B(黑)
     /   \ 
    X    C(黑)
c. w是黑色,其子结点左红右黑
              A
            /   \
       -1 x(黑)  w(黑)
          	   /   \
          	 C(红)  B(黑)
  1. 交换w和w左结点的颜色
  2. w进行右旋
  3. w 的父结点设置为新的w 则变成了情况 b
              A
            /   \
       -1 x(黑)  w(黑)
          	   /   \
          	原c左    C(红)
          	 	 /	 \
          	 原c的	 B(黑)
          	 右结点	 
    注意次处并没有交换结点,只是改变了操作的对象
d. w是红色的
          A(黑)
        /   \
   -1 x(黑)  w(红)
      	   /   \
      	  C     B 
  1. w和x的父结点,颜色对掉
  2. 对x的父结点,进行一次左旋
  3. 把x的新兄弟设成w

这样不破坏红黑性质,就把,d 转化成了前面 三种情况之1

         C(黑)
        /   \
     A(红)    B 
     /   \
-1 X(黑)  W(黑)
    

下面是go的实现:

func (t *RBTree) AddNode(ele int) {
	node := t.BSAddNode(ele)
	t.AddNodeFixUp(node)
}

func (t *RBTree) DelNode(ele int) {
	node := t.FindNode(ele)
	if node == nil { //没找到不用删除
		return
	}
	// 转化成删除后继结点
	nextNode := node.FindNext()
	nextNode.Data, node.Data = node.Data, nextNode.Data

	var sNode *RBNode
	if nextNode.Left != nil {
		sNode = nextNode.Left
	} else {
		sNode = nextNode.Right
	}
	sNode.Parent = nextNode.Parent
	//nextNode  是根结点
	if nextNode.Parent == nil {
		t.Root = sNode
	} else if nextNode == nextNode.Parent.Left {
		nextNode.Parent.Left = sNode
	} else {
		nextNode.Parent.Right = sNode
	}

	if nextNode.Color == ColorBlack { //处理删除黑色结点的情况
		t.FixDel(sNode)
	}
}

func (t *RBTree) FixDel(x *RBNode) {
	for x != t.Root && x.Color == ColorBlack {
		if x == x.Parent.Left {//x再左边
			w := x.Parent.Right
			if w.Color == ColorRed {
				 w.Color = ColorBlack
				 x.Parent.Color = ColorRed
				 x.Parent.LeftRotate(t)
				 w = x.Parent.Right
			}
			if w.Left.Color == ColorBlack && w.Right.Color == ColorBlack {
				w.Color = ColorRed
				x = x.Parent
			} else {
				if w.Right.Color == ColorBlack {
					w.Left.Color = ColorBlack
					w.Color = ColorRed
					w.RightRotate(t)
					w = x.Parent.Right
				}
				w.Color = x.Parent.Color
				x.Parent.Color = ColorBlack
				w.Right.Color = ColorBlack
				x.Parent.LeftRotate(t)
				x = t.Root
			}
		} else{
			w := x.Parent.Left
			if w.Color == ColorRed {
				w.Color = ColorBlack
				x.Parent.Color = ColorRed
				x.Parent.RightRotate(t)
				w = x.Parent.Right
			}

			if w.Left.Color == ColorBlack && w.Right.Color == ColorBlack {
				w.Color = ColorRed
				x = x.Parent
			} else {
				if w.Right.Color == ColorBlack {
					w.Left.Color = ColorBlack
					w.Color = ColorRed
					w.LeftRotate(t)
					w = x.Parent.Left
				}
				w.Color = x.Parent.Color
				x.Parent.Color = ColorBlack
				w.Right.Color = ColorBlack
				x.Parent.RightRotate(t)
				x = t.Root
			}
		}
	}

}



func (t *RBTree) FindNode(ele int) *RBNode {
	if t.Root == nil {
		return nil
	}
	node := t.Root

	for node != nil {
		if node.Data > ele {
			node = node.Left
		} else if node.Data < ele {
			node = node.Right
		} else {
			return node
		}
	}
	return nil
}

func (n *RBNode) FindNext() *RBNode {
	if n.Right == nil {
		return n
	}
	travl := n.Right
	for travl.Left != nil {
		travl = travl.Left
	}
	return travl
}

代码示例: gitee.com/gudongkun/datestruct

代码实现:参考 https://gitee.com/hoemfei/RBTree/blob/master/RBTree.go

视频参考:

红黑树快速入门:https://www.bilibili.com/video/BV18y4y1m721

红黑树变色和旋转:https://www.bilibili.com/video/BV1aK4y1W7yj

红黑树插入:https://www.bilibili.com/video/BV1nf4y1z7nP

观看此视频时,注意,查入结点的父结点是红色时,才需要修复,黑色不需要修复。

红黑树的删除:https://www.bilibili.com/video/BV1uZ4y1P7rr

观看此视频时,注意,平衡二叉树,的第三种情况,都会被转化成第一或第二种情况,所以, 被删除结点只会有一个子结点 那就是 x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值