38 / 69

Deletion from a Red-Black Tree

Qizil-qora daraxtdan elementni o'chirish

Qizil-qora daraxtdan (Red-Black Tree) tugunni o'chirish ikkilik qidiruv daraxtidan element o'chirishga o'xshaydi, biroq o'chirilgandan so'ng qizil-qora xususiyatlari (ayniqsa qora chuqurlik) buzilmasligi uchun qayta bo'yash va burish amallari talab qilinadi.

Agar o'chirilgan tugun QIZIL bo'lsa, qizil-qora daraxt qoidalari buzilmaydi (chunki qora chuqurlik o'zgarmaydi va yangi qizil-qizil ketma-ketlik hosil bo'lmaydi). Ammo agar QORA tugun o'chirilsa, qora chuqurlik qoidasi buziladi va uni tuzatish uchun DeleteFix algoritmi ishga tushiriladi.


Tugunni o'chirish bosqichlari

O'chirish jarayoni quyidagi ketma-ketlikda amalga oshiriladi:

  1. O'chiriladigan tugun nodeToBeDeleted topiladi:

O'chiriladigan tugun

O'chiriladigan tugun (nodeToBeDeleted)

  1. nodeToBeDeleted ning rangi originalColor o'zgaruvchisida saqlab qolinadi:

Asl rangni saqlash

Asl rangni saqlab qolish

  1. Agar chap farzand NULL bo'lsa:
    x ga o'ng farzand tayinlanadi va nodeToBeDeleted o'rniga x ko'chiriladi (transplant):

x ni o'ng farzandga belgilash

x ni o'ng farzandga yo'naltirish

nodeToBeDeleted o'rniga x ni ko'chirish

nodeToBeDeleted o'rniga x ni ko'chirish

  1. Agar o'ng farzand NULL bo'lsa:
    x ga chap farzand tayinlanadi va nodeToBeDeleted o'rniga x ko'chiriladi.

  2. Agar ikkala farzand ham mavjud bo'lsa:
    O'ng qism-daraxtning eng kichik tuguni y (inorder successor) topiladi. y ning rangi originalColor ga saqlanadi, uning o'ng farzandi x bo'ladi. y tugun nodeToBeDeleted o'rniga ko'chiriladi va unga nodeToBeDeleted ning asl rangi beriladi.

  3. Agar originalColor QORA bo'lsa, DeleteFix(x) chaqiriladi.


O'chirishdan so'ng qoidalarni tiklash algoritmi

Qora tugun o'chirilganda, x tuguniga "qo'shimcha qora" (extra black yoki double black) yuklangan deb faraz qilinadi. Bu qo'shimcha qorani ildizga yetkazish, qizil-qora tugunga berish yoki burish/bo'yash orqali yo'qotish kerak.

x daraxtning ildizi bo'lmaguniga va uning rangi QORA bo'lib turguniga qadar quyidagi holatlar ko'rib chiqiladi (x chap farzand bo'lgan hol uchun, o'ng farzand holi simmetrikdir):

w — x ning qondosh tuguni (qo'shnisi / sibling):

w tugunini belgilash

x ning qo'shnisi w ni aniqlash

1-Holat: Qo'shni w QIZIL bo'lsa

  • w ning rangini QORA, x ning otasini QIZIL qiling.
  • x ning otasi ustida chapga burish (Left-Rotate) bajaring.
  • w ni x otasining yangi o'ng farzandiga yo'naltiring:

Ranglarni o'zgartirish

w ni qora, otani qizil qilish

Chapga burish

Ota tugunni chapga burish

w ni qayta yo'naltirish

w ni yangi o'ng farzandga yo'naltirish

2-Holat: Qo'shni w QORA va uning ikkala farzandi ham QORA bo'lsa

  • w ning rangini QIZIL qilib belgilang.
  • x ni uning otasiga ko'chiring (x = x.parent).

3-Holat: Qo'shni w QORA, uning chap farzandi QIZIL va o'ng farzandi QORA bo'lsa

  • w ning chap farzandini QORA, w ning o'zini QIZIL qiling.
  • w ustida o'ngga burish (Right-Rotate) bajaring.
  • w ni x otasining yangi o'ng farzandiga yo'naltiring:

Ranglarni o'zgartirish

w ning chap farzandini qora, w ni qizil qilish

O'ngga burish

w ustida o'ngga burish

w ni yangilash

w ni yangi o'ng farzandga yo'naltirish

4-Holat: Qo'shni w QORA va uning o'ng farzandi QIZIL bo'lsa

  • w ga x ning otasining rangini bering.
  • x ning otasini va w ning o'ng farzandini QORA qiling.
  • x ning otasi ustida chapga burish (Left-Rotate) bajaring.
  • x ni ildiz qilib belgilang (sikl tugaydi):

Ranglarni yangilash

Ranglarni o'tkazish va yangilash

Chapga burish

Ota tugunni chapga burish

x ni ildiz deb belgilash

x ni daraxt ildizi deb belgilash

Yakunda x ning rangini har doim QORA qilib belgilang.


O'chirish amallarining blok-sxemasi

Yuqoridagi barcha 4 ta holatning umumiy mantiqiy ketma-ketligini quyidagi blok-sxema orqali tushunish mumkin:

Qizil-qora daraxtdan o'chirish blok-sxemasi

Qizil-qora daraxtdan o'chirish algoritmi blok-sxemasi


Qizil-qora daraxtdan o'chirish dasturiy kodi

package main

import "fmt"

type Color bool

const (
	RED   Color = true
	BLACK Color = false
)

type Node struct {
	data   int
	color  Color
	left   *Node
	right  *Node
	parent *Node
}

type RedBlackTree struct {
	TNULL *Node
	root  *Node
}

func NewRedBlackTree() *RedBlackTree {
	tnull := &Node{color: BLACK}
	return &RedBlackTree{
		TNULL: tnull,
		root:  tnull,
	}
}

func (tree *RedBlackTree) leftRotate(x *Node) {
	y := x.right
	x.right = y.left
	if y.left != tree.TNULL {
		y.left.parent = x
	}
	y.parent = x.parent
	if x.parent == nil {
		tree.root = y
	} else if x == x.parent.left {
		x.parent.left = y
	} else {
		x.parent.right = y
	}
	y.left = x
	x.parent = y
}

func (tree *RedBlackTree) rightRotate(x *Node) {
	y := x.left
	x.left = y.right
	if y.right != tree.TNULL {
		y.right.parent = x
	}
	y.parent = x.parent
	if x.parent == nil {
		tree.root = y
	} else if x == x.parent.right {
		x.parent.right = y
	} else {
		x.parent.left = y
	}
	y.right = x
	x.parent = y
}

func (tree *RedBlackTree) insertFix(k *Node) {
	for k.parent != nil && k.parent.color == RED {
		if k.parent == k.parent.parent.right {
			u := k.parent.parent.left
			if u.color == RED {
				u.color = BLACK
				k.parent.color = BLACK
				k.parent.parent.color = RED
				k = k.parent.parent
			} else {
				if k == k.parent.left {
					k = k.parent
					tree.rightRotate(k)
				}
				k.parent.color = BLACK
				k.parent.parent.color = RED
				tree.leftRotate(k.parent.parent)
			}
		} else {
			u := k.parent.parent.right
			if u.color == RED {
				u.color = BLACK
				k.parent.color = BLACK
				k.parent.parent.color = RED
				k = k.parent.parent
			} else {
				if k == k.parent.right {
					k = k.parent
					tree.leftRotate(k)
				}
				k.parent.color = BLACK
				k.parent.parent.color = RED
				tree.rightRotate(k.parent.parent)
			}
		}
		if k == tree.root {
			break
		}
	}
	tree.root.color = BLACK
}

func (tree *RedBlackTree) Insert(key int) {
	node := &Node{
		data:   key,
		color:  RED,
		left:   tree.TNULL,
		right:  tree.TNULL,
		parent: nil,
	}

	var y *Node = nil
	x := tree.root

	for x != tree.TNULL {
		y = x
		if node.data < x.data {
			x = x.left
		} else {
			x = x.right
		}
	}

	node.parent = y
	if y == nil {
		tree.root = node
	} else if node.data < y.data {
		y.left = node
	} else {
		y.right = node
	}

	if node.parent == nil {
		node.color = BLACK
		return
	}

	if node.parent.parent == nil {
		return
	}

	tree.insertFix(node)
}

func (tree *RedBlackTree) rbTransplant(u, v *Node) {
	if u.parent == nil {
		tree.root = v
	} else if u == u.parent.left {
		u.parent.left = v
	} else {
		u.parent.right = v
	}
	v.parent = u.parent
}

func (tree *RedBlackTree) minimum(node *Node) *Node {
	for node.left != tree.TNULL {
		node = node.left
	}
	return node
}

func (tree *RedBlackTree) fixDelete(x *Node) {
	for x != tree.root && x.color == BLACK {
		if x == x.parent.left {
			s := x.parent.right
			if s.color == RED {
				s.color = BLACK
				x.parent.color = RED
				tree.leftRotate(x.parent)
				s = x.parent.right
			}

			if s.left.color == BLACK && s.right.color == BLACK {
				s.color = RED
				x = x.parent
			} else {
				if s.right.color == BLACK {
					s.left.color = BLACK
					s.color = RED
					tree.rightRotate(s)
					s = x.parent.right
				}

				s.color = x.parent.color
				x.parent.color = BLACK
				s.right.color = BLACK
				tree.leftRotate(x.parent)
				x = tree.root
			}
		} else {
			s := x.parent.left
			if s.color == RED {
				s.color = BLACK
				x.parent.color = RED
				tree.rightRotate(x.parent)
				s = x.parent.left
			}

			if s.right.color == BLACK && s.left.color == BLACK {
				s.color = RED
				x = x.parent
			} else {
				if s.left.color == BLACK {
					s.right.color = BLACK
					s.color = RED
					tree.leftRotate(s)
					s = x.parent.left
				}

				s.color = x.parent.color
				x.parent.color = BLACK
				s.left.color = BLACK
				tree.rightRotate(x.parent)
				x = tree.root
			}
		}
	}
	x.color = BLACK
}

func (tree *RedBlackTree) Delete(data int) {
	tree.deleteNodeHelper(tree.root, data)
}

func (tree *RedBlackTree) deleteNodeHelper(node *Node, key int) {
	var z *Node = tree.TNULL
	var x, y *Node

	for node != tree.TNULL {
		if node.data == key {
			z = node
		}
		if node.data <= key {
			node = node.right
		} else {
			node = node.left
		}
	}

	if z == tree.TNULL {
		fmt.Printf("Kalit %d daraxtda topilmadi\n", key)
		return
	}

	y = z
	yOriginalColor := y.color
	if z.left == tree.TNULL {
		x = z.right
		tree.rbTransplant(z, z.right)
	} else if z.right == tree.TNULL {
		x = z.left
		tree.rbTransplant(z, z.left)
	} else {
		y = tree.minimum(z.right)
		yOriginalColor = y.color
		x = y.right
		if y.parent == z {
			x.parent = y
		} else {
			tree.rbTransplant(y, y.right)
			y.right = z.right
			y.right.parent = y
		}

		tree.rbTransplant(z, y)
		y.left = z.left
		y.left.parent = y
		y.color = z.color
	}
	if yOriginalColor == BLACK {
		tree.fixDelete(x)
	}
}

func (tree *RedBlackTree) printHelper(root *Node, indent string, last bool) {
	if root != tree.TNULL {
		fmt.Print(indent)
		if last {
			fmt.Print("R----")
			indent += "   "
		} else {
			fmt.Print("L----")
			indent += "|  "
		}
		sColor := "RED"
		if root.color == BLACK {
			sColor = "BLACK"
		}
		fmt.Printf("%d(%s)\n", root.data, sColor)
		tree.printHelper(root.left, indent, false)
		tree.printHelper(root.right, indent, true)
	}
}

func (tree *RedBlackTree) PrintTree() {
	tree.printHelper(tree.root, "", true)
}

func main() {
	tree := NewRedBlackTree()
	elements := []int{55, 40, 65, 60, 75, 57}
	for _, e := range elements {
		tree.Insert(e)
	}

	fmt.Println("Boshlang'ich daraxt:")
	tree.PrintTree()

	tree.Delete(40)
	fmt.Println("\n40 o'chirilgandan so'ng:")
	tree.PrintTree()
}

O'chirish murakkabligi

  • Vaqt murakkabligi (Time Complexity): O(log n) — o'chiriladigan tugunni qidirish va daraxtni burish/qayta bo'yash amallari ko'pi bilan O(log n) vaqt oladi.
  • Xotira murakkabligi (Space Complexity): O(n).