左倾红黑树Go语言实现

左倾红黑树的定义

含有红黑链接(边)并满足下列条件的二叉查找树:

  • 红链接均为左链接
  • 没有任何一个结点同时和两条红链接相连;
  • 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

红链接将两个2-结点(普通二叉树结点)链接起来构成一个3-结点(含有两个键,3个链接);黑链接则是2-3树中的普通链接。

满足这样定义的红黑树和相应的2-3树是一一对应的。 将红链接画平时,一颗红黑树就是一颗2-3树

在这里插入图片描述

红黑树性质

(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),性质5保证红黑树中最大长度不超过最小长度2倍 。(最小长度全部由黑结点组成,最大长度由红黑结点交替构成)。

将左倾红黑树转化为2-3树就能明白上述性质。

Node数据结构

将链接的颜色保存在表示结点的Node数据类型的布尔变量color中;如果指向它的链接是红色的,那么该变量为true,黑色则为false。

当我们提到一个结点的颜色时,指的是指向该结点的链接的颜色。

    type Color bool
    
    type Key string
    type Value int
    
    const (
    	RED   = true
    	BLACK = false
    )
    
    type RBT struct {
    	root *Node
    }
    
    // 红链接将两个2-结点链接起来构成一个3-结点
    // 黑链接则是2-3树中的普通链接。
    type Node struct {
    	key         Key
    	val         Value
    	left, right *Node
    	color       Color // 由其父结点指向它的链接的颜色,true-红链接,false-黑链接
    	n           int   // 这棵子树中的结点总书
    }
    
    func NewNode(key Key, val Value, n int, color Color) *Node {
    	return &Node{
    		key:   key,
    		val:   val,
    		color: color,
    		n:     n,
    	}
    }

旋转


在这里插入图片描述

插入

新插入的结点,总是用红链接将其与它的父结点相连;

插入新结点后:

  1. 使得父结点有两条红链接,那么直接将其转为两个黑链接即可;(0次旋转)
  2. 如果产生一条红色的右链接,就对父结点执行左旋操作;(1次旋转)
  3. 如果产生连续的两条红右链接,就对上层的红链接执行一次右旋转,转为情况1;(1次旋转)
  4. 如果产生连续的两条连续的红链接,一条红色左链接,一条红色右链接;只需将下层的红色右链接执行一次左旋转,转为情况3;(2次旋转)
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

颜色转换

在这里插入图片描述

删除

参考:

实现

    package red_black
    
    type Color bool
    
    type Key int
    type Value int
    
    const (
    	RED   = true
    	BLACK = false
    )
    
    type RBT struct {
    	root *Node
    }
    
    // 红链接将两个2-结点链接起来构成一个3-结点
    // 黑链接则是2-3树中的普通链接。
    type Node struct {
    	key         Key
    	val         Value
    	left, right *Node
    	color       Color // 由其父结点指向它的链接的颜色,true-红链接,false-黑链接
    	size        int
    }
    
    func NewNode(key Key, val Value, color Color, size int) *Node {
    	return &Node{
    		key:   key,
    		val:   val,
    		left:  nil,
    		right: nil,
    		color: color,
    		size:  size,
    	}
    }
    
    func isRed(node *Node) bool {
    	if node == nil {
    		return false
    	}
    	return node.color == RED
    }
    
    func (this *RBT) isEmpty() bool {
    	return this.root == nil
    }
    
    func size(h *Node) int {
    	if h == nil {
    		return 0
    	}
    	return h.size
    }
    
    func (this *RBT) Size() int {
    	return size(this.root)
    }
    
    func findMin(h *Node) *Node {
    
    	for h.left != nil {
    		h = h.left
    	}
    	return h
    }
    
    // 左旋
    func rotateLeft(h *Node) *Node {
    	x := h.left
    	h.left = x.right
    	x.right = h
    	x.color = h.color
    	h.color = RED
    	x.size = h.size
    	h.size = 1 + size(h.left) + size(h.right)
    	return x
    }
    
    // 右旋
    func rotateRight(h *Node) *Node {
    	x := h.right
    	h.right = x.left
    	x.left = h
    	x.color = h.color
    	h.color = RED
    	x.size = h.size
    	h.size = 1 + size(h.left) + size(h.right)
    	return x
    }
    
    func flipsColor(h *Node) {
    	h.color = !h.color
    	h.left.color = !h.left.color
    	h.right.color = !h.right.color
    }
    
    func fixUp(h *Node) *Node {
    
    	// 存在红色右链接,需要右转
    	if !isRed(h.left) && isRed(h.right) {
    		h = rotateRight(h)
    	}
    	// 存在两天连续的红色左链接,需要左转
    	if isRed(h.left) && isRed(h.left.left) {
    		h = rotateLeft(h)
    	}
    	if isRed(h.left) && isRed(h.right) {
    		flipsColor(h)
    	}
    	h.size = 1 + size(h.left) + size(h.right)
    	return h
    }
    
    func (this *RBT) Put(key Key, val Value) {
    	this.root = put(this.root, key, val)
    	this.root.color = BLACK
    }
    
    func put(h *Node, key Key, val Value) *Node {
    	if h == nil {
    		return NewNode(key, val, RED, 1)
    	}
    
    	if key < h.key {
    		h.left = put(h.left, key, val)
    	} else if key > h.key {
    		h.right = put(h.right, key, val)
    	} else {
    		h.key = key
    		h.val = val
    	}
    	return fixUp(h)
    }
    
    // 让左孩子结点变红,即3-结点
    func moveRedLeft(h *Node) *Node {
    	flipsColor(h)
    	// 如果h的右子结点是一个2-结点,只用反转颜色即可
    	// 若h的右子结点是个3-结点,需要从h的右子结点借一个给h的左子结点
    	if isRed(h.right.left) {
    		// 左旋右子节点
    		h.right = rotateLeft(h.right)
    		// 右旋h
    		h = rotateRight(h)
    		// 颜色翻转
    		flipsColor(h)
    	}
    	return h
    }
    
    // 让右子结点变成红结点,即3-结点
    func moveRedRight(h *Node) *Node {
    
    	flipsColor(h)
    	// h.right是一个2-结点
    	// h.left是h.right的兄弟结点,且h.left是一个3-结点,需要借给h.left一个键
    	// 这里只进行一次左旋即可
    	if isRed(h.left.left) {
    		h = rotateLeft(h)
    		flipsColor(h)
    	}
    	return h
    }
    
    // delete 就是在查找路径上进行和deleteMin、deleteMax相同的变换
    // 要求保证在查找过程中任意当前结点不是2-结点。
    // 如果被删除的值在树的底部,我们可以直接删除它。
    // 如果不在,我们需要和二叉查找树中的删除一样:
    // 用其右子树的最小的值代替该节点的值,并删除那个节点(右子树的最小的值)。
    // 然后问题已经转化为在一棵根节点的子树中删除最小值,可以使用上面写的删除最小值的方法。
    func delete(h *Node, key Key) *Node {
    
    	if key < h.key {
    
    		// 向左子树搜索,保证左节点不是2-结点
    		if !isRed(h.left) && !isRed(h.left.left) {
    			h = moveRedLeft(h)
    		}
    		h.left = delete(h.left, key)
    	} else {
    
    		// h.left为红便左旋,使得h.right变红
    		if isRed(h.left) {
    			h = rotateLeft(h)
    		}
    
    		// 找到目标结点
    		if key == h.key && h.right == nil {
    			// 左子数不为红,且右子树为空,那么左子数只能是空;不然黑节点的高度不平衡
    			return nil
    		}
    
    		// 无论如何都要删除右子树中的结点
    		// 	如果要删的是h,与右子树的min对调后删除右子树的min
    		// 	否则目标节点在右子树中
    		// 所以需要让右子结点变红
    		if !isRed(h.right) && !isRed(h.right.right) {
    			h = moveRedRight(h)
    		}
    
    		// 找到目标节点,将其替换为右子树的最小值
    		// 并删除右子树的最小值
    		if key == h.key {
    			t := findMin(h.right)
    			h.key = t.key
    			h.val = t.val
    			h.right = delete(h.right, t.key) // 等同于 deleteMin(h.right)
    		} else {
    			h.right = delete(h.right, key)
    		}
    	}
    	return fixUp(h)
    }
    
    func (this *RBT) Delete(key Key) {
    	if !this.Contain(key) {
    		return
    	}
    	this.root = delete(this.root, key)
    	if this.root != nil {
    		this.root.color = BLACK
    	}
    }

Keys

    func keys(h *Node, res *[]Value) {
    	if h == nil {
    		return
    	}
    	keys(h.left, res)
    	*res = append(*res, h.val)
    	keys(h.right, res)
    	return
    }
    
    func (this *RBT) Keys() []Value {
    	ans := make([]Value, 0)
    	keys(this.root, &ans)
    	return ans
    }

Contains

    func contrains(h *Node, key Key) *Node {
    	if h == nil {
    		return nil
    	}
    
    	if key == h.key {
    		return h
    	} else if key < h.key {
    		return contrains(h.left, key)
    	} else {
    		return contrains(h.right, key)
    	}
    }
    
    func (this *RBT) Contains(key Key) bool {
    	return contrains(this.root, key) != nil
    }

DeleteMin、DeleteMax

    func deleteMax(h *Node) *Node {
    	// h.left是一个红节点,不是h.right的兄弟结点
    	// 需要向左旋转,使得h.left变红,方便从右子树里删除
    	if isRed(h.left) {
    		h = rotateLeft(h)
    	}
    	if h.right == nil {
    		return nil
    	} else if !isRed(h.right) && !isRed(h.right.left) {
    		// h.right 是一个 2-结点
    		// 两种情况:
    		// 1. h.left也是一个2-结点,秩序反转颜色即可
    		// 2. h.left是一个3-结点,需要从
    		h = moveRedRight(h)
    	}
    	h.right = deleteMax(h)
    	return fixUp(h)
    }
    
    func (this *RBT) DeleteMax() {
    	this.root = deleteMax(this.root)
    	if this.root != nil {
    		this.root.color = BLACK
    	}
    }
    
    func deleteMin(h *Node) *Node {
    	if h.left == nil {
    		return nil
    	}
    	// 左子结点是一个2-结点
    	if !isRed(h.left) && !isRed(h.left.left) {
    		// 两种情况:
    		// 1. 右子结点也是一个2-结点,直接颜色翻转即可
    		// 2. 右子结点是一个3-结点,从右子结点借一个给左子结点
    		h = moveRedLeft(h)
    	}
    	h.left = deleteMin(h.left)
    	return fixUp(h)
    }
    
    func (this *RBT) DeleteMin() {
    	this.root = deleteMin(this.root)
    	if this.root != nil {
    		this.root.color = BLACK
    	}
    	return
    }

Rank、Get

Rank(key Key)返回key的排名,从0开始计数

Get(index int)返回排名为index的key,index从0开始计数

    // 返回排名为rank的结点
    // rank从0开始
    func get(h *Node, rank int) *Node {
       if h == nil || rank == size(h.left) {
          return h
       }
    
       lsize := size(h.left)
       if rank > lsize {
          return get(h.right, rank-lsize-1)
       } else {
          return get(h.left, rank)
       }
    }
    
    func (this *RBT) Get(rank int) *Node {
       if rank < 0 || rank >= this.Size() {
          return nil
       }
       return get(this.root, rank)
    }
    
    // 返回key的排名,从0开始计数
    func rank(h *Node, key Key) int {
       // 返回以h为根节点的子树中小于h.key的键的数量
       if h == nil {
          return 0
       }
       if key == h.key {
          return size(h.left)
       } else if key < h.key {
          return rank(h.left, key)
    
       } else {
          return 1 + size(h.left) + rank(h.right, key)
       }
    }
    
    // 红黑树中不存在key返回-1
    func (this *RBT) Rank(key Key) int {
       if !this.Contain(key) {
          return -1
       }
       return rank(this.root, key)
    }

Ceil

    func ceil(h *node, key int) *node {
    	if h == nil {
    		return nil
    	}
    	
        // h 可能是一个合格的结点,向左尝试找 key <= x < h.key
    	if h.key >= key {
    		tmp := ceil(h.left, key)
    		if tmp != nil {
    			return tmp
    		}
    		return h
    	} else {
    		return ceil(h.right, key)
    	}
    
    }
    
    func (this *RBT) Ceil(key int) (bool, int) {
    	n := ceil(this.root, key)
    	if n == nil {
    		return false, -1
    	}
    	return true, n.val
    }

Floor

    func floor(h *node, key int) *node {
    	if h == nil {
    		return nil
    	}
        // h 可能是一个合格的结点,也可以向右尝试找 h.key < x <= h.key
    	if h.key <= key {
    		tmp := floor(h.right, key)
    		if tmp != nil {
    			return tmp
    		}
    		return h
    	} else {
    		return floor(h.left, key)
    	}
    
    }
    
    func (this *RBT) Floor(key int) (bool, int) {
    	n := ceil(this.root, key)
    	if n == nil {
    		return false, -1
    	}
    	return true, n.val
    }

例题

  • leetcode 220. 存在重复元素 III:红黑树的添加与删除,实现Ceil(key)找到第一个大于key的数,Floor(key)找到最后一个小于key的数。
  • leetcode 436 寻找右区间:红黑树的添加、Ceil(key)实现;将区间的左端点作为key,下标作为val构建红黑树;多于每一个区间[start,edn]调用Ceil(end)找到第一个大于等于end的左区间端点,并返回其下标即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值