34 / 69

Insertion on a B+ Tree

B+ daraxtga element qo'shish

B+ daraxtga element qo'shish uchta asosiy bosqichdan iborat: mos barg (leaf) tugunni qidirish, elementni kiritish va zarur hollarda daraxtni muvozanatlash / ikkiga bo'lish (splitting).

Keling, ushbu jarayonlarni batafsil tushunib olaylik.


Qo'shish amali

B+ daraxtga element kiritishdan oldin quyidagi xususiyatlarni yodda tutish kerak:

  • Ildiz tugun kamida 2 ta farzandga ega bo'ladi.
  • Ildizdan tashqari har bir tugun eng ko'pi bilan m ta va kamida m / 2 ta farzandga ega bo'lishi mumkin.
  • Har bir tugun ko'pi bilan m - 1 ta kalit va kamida ⌈m / 2⌉ - 1 ta kalitni saqlashi mumkin.

Element qo'shish uchun quyidagi qadamlar bajariladi:

  1. B+ daraxtda barcha elementlar faqat barg tugunlarga kiritilganligi sababli, avval mos barg tuguniga o'ting.
  2. Kalitni barg tugunga kiriting.

1-Holat: Barg to'lmagan bo'lsa

Agar barg tugun to'lmagan bo'lsa, kalitni barg tugunga o'sish tartibini saqlagan holda kiriting.

2-Holat: Barg to'lgan bo'lsa

Agar barg tugun to'lgan bo'lsa, quyidagi usulda daraxt muvozanatlanadi:

  1. Kalitni barg tugunga o'sish tartibida kiriting.
  2. Tugunni m / 2 pozitsiyasi bo'yicha ikkiga bo'ling (split).
  3. m / 2-chi kalit nusxasini ota (parent) tugunga ham qo'shing.
  4. Agar ota tugun ham to'lgan bo'lsa, xuddi shu bo'lish qadamlarini ota tugun uchun ham takrorlang.

Qo'shish misoli

Quyidagi rasmli misolda 5, 15, 25, 35, 45 elementlarini tartibi m = 4 bo'lgan B+ daraxtga kiritish jarayonini ko'rib chiqamiz:

  1. 5 ni kiritish:

5 ni kiritish

5 ni kiritish

  1. 15 ni kiritish:

15 ni kiritish

15 ni kiritish

  1. 25 ni kiritish:

25 ni kiritish

25 ni kiritish

  1. 35 ni kiritish:

35 ni kiritish

35 ni kiritish

  1. 45 ni kiritish:

45 ni kiritish

45 ni kiritish va tugunni bo'lish jarayoni


B+ daraxtga element qo'shish dasturiy kodi

package main

import "fmt"

// BPNode B+ daraxt tugun strukturasi
type BPNode struct {
	isLeaf   bool
	keys     []int
	children []*BPNode
	next     *BPNode // Barg tugunlar uchun bog'langan ro'yxat ko'rsatkichi
}

func newBPNode(isLeaf bool) *BPNode {
	return &BPNode{
		isLeaf:   isLeaf,
		keys:     make([]int, 0),
		children: make([]*BPNode, 0),
		next:     nil,
	}
}

// BPlusTree strukturasi
type BPlusTree struct {
	root  *BPNode
	order int // Har bir tugundagi maksimal farzandlar soni (m)
}

func NewBPlusTree(order int) *BPlusTree {
	root := newBPNode(true)
	return &BPlusTree{
		root:  root,
		order: order,
	}
}

// Search - B+ daraxtda kalitni qidirish
func (tree *BPlusTree) Search(k int) bool {
	curr := tree.root
	for !curr.isLeaf {
		i := 0
		for i < len(curr.keys) && k >= curr.keys[i] {
			i++
		}
		curr = curr.children[i]
	}

	for _, key := range curr.keys {
		if key == k {
			return true
		}
	}
	return false
}

// Insert - B+ daraxtga element qo'shish
func (tree *BPlusTree) Insert(k int) {
	root := tree.root
	if len(root.keys) == tree.order-1 {
		newRoot := newBPNode(false)
		newRoot.children = append(newRoot.children, root)
		tree.splitChild(newRoot, 0, root)
		tree.root = newRoot
		tree.insertNonFull(newRoot, k)
	} else {
		tree.insertNonFull(root, k)
	}
}

func (tree *BPlusTree) insertNonFull(node *BPNode, k int) {
	if node.isLeaf {
		idx := 0
		for idx < len(node.keys) && node.keys[idx] < k {
			idx++
		}
		if idx < len(node.keys) && node.keys[idx] == k {
			return // Takroriy qiymat kiritilmaydi
		}
		node.keys = append(node.keys, 0)
		for j := len(node.keys) - 1; j > idx; j-- {
			node.keys[j] = node.keys[j-1]
		}
		node.keys[idx] = k
		return
	}

	idx := 0
	for idx < len(node.keys) && k >= node.keys[idx] {
		idx++
	}

	child := node.children[idx]
	if len(child.keys) == tree.order-1 {
		tree.splitChild(node, idx, child)
		if k >= node.keys[idx] {
			idx++
		}
	}
	tree.insertNonFull(node.children[idx], k)
}

func (tree *BPlusTree) splitChild(parent *BPNode, idx int, child *BPNode) {
	mid := len(child.keys) / 2
	promotedKey := child.keys[mid]

	newNode := newBPNode(child.isLeaf)

	if child.isLeaf {
		newNode.keys = append(newNode.keys, child.keys[mid:]...)
		child.keys = child.keys[:mid]

		newNode.next = child.next
		child.next = newNode
	} else {
		newNode.keys = append(newNode.keys, child.keys[mid+1:]...)
		newNode.children = append(newNode.children, child.children[mid+1:]...)

		child.keys = child.keys[:mid]
		child.children = child.children[:mid+1]
	}

	parent.children = append(parent.children, nil)
	for j := len(parent.children) - 1; j > idx+1; j-- {
		parent.children[j] = parent.children[j-1]
	}
	parent.children[idx+1] = newNode

	parent.keys = append(parent.keys, 0)
	for j := len(parent.keys) - 1; j > idx; j-- {
		parent.keys[j] = parent.keys[j-1]
	}
	parent.keys[idx] = promotedKey
}

// PrintLeaves - Barg tugunlar zanjirini chiqarish
func (tree *BPlusTree) PrintLeaves() {
	curr := tree.root
	for !curr.isLeaf {
		curr = curr.children[0]
	}

	fmt.Print("B+ daraxt barglari (bog'langan ro'yxat): ")
	for curr != nil {
		for _, k := range curr.keys {
			fmt.Printf("%d ", k)
		}
		curr = curr.next
	}
	fmt.Println()
}

func main() {
	tree := NewBPlusTree(4) // order = 4
	elements := []int{5, 15, 25, 35, 45, 55, 40, 30, 20}
	for _, e := range elements {
		tree.Insert(e)
	}

	tree.PrintLeaves()

	for _, k := range []int{45, 15, 99} {
		if tree.Search(k) {
			fmt.Printf("Kalit %d: topildi\n", k)
		} else {
			fmt.Printf("Kalit %d: topilmadi\n", k)
		}
	}
}

Qo'shish murakkabligi

  • Vaqt murakkabligi (Time Complexity): Θ(t * log_t n). Bu asosan mos barg tugunni qidirish vaqti Θ(log_t n) bilan belgilanadi.
  • Xotira murakkabligi (Space Complexity): Θ(n).