• Strom je parametrizován přirozeným číslem
  • B strom je definován následujícími podmínkami:
    • V uzlech může být uloženo více klíčů, maximálně však . Ve všech uzlech mimo kořene však musí být uloženo minimálně klíčů (pokud je strom neprázdný je v kořeni vždy alespoň jeden klíč)
    • Pokud uzel obsahuje klíčů, má (je list) nebo potomků
      • Všechny listy ve stromu jsou ve stejné hloubce
    • Klíče jsou v uzlu uspořádány vzestupně
      • ()
  • Výška B-stromu: B strom s klíči a má výšku nejvýše
  • B-stromy jsou často používány v databázových systémech.
    • Zaměřují se na vlastnost snížení operací s diskem.

Implementace

struct node {
  keys, // pole klicu o velikosti 2t-1
  children, // pole pointeru na potomky o velikosti 2t
  parent, // pointer na rodiče
  n, // počet klíčů v uzlu
  leaf, // priznak toho, jestli je uzel listem
  data, // pointer na satelitni data
  …
}
struct tree {
  root, // korenovy uzel
}
proc create-empty-tree(T)
//prázdný strom reprezentujeme pomocí kořenového uzlu, který neobsahuje žádný klíč
  x = new_node()
  x.leaf = true
  x.n = 0
  T.root = x

Operace s B-stromy

Vyhledávání -

  • Zobecnění vyhledávání ve vyhledávacím stromě. Rozhodování, do kterého podstromu se vydáme je nyní založeno na porovnávání s polem klíčů, nikoliv jenom s jedním klíčem.
  • Neúspěsné vyhledávání končí v listu.
proc b-tree-search(x,k)
  i = 0
  while i < x.n and k > x.keys[i]
  // kdyz cyklus skonci, bud i == x.n nebo k <= x.keys[i]
    i += 1
  // cyklus skončil protoze k == x.keys[i]
  if i < x.n and k == x.keys[i] 
    return x, i
 
  // nenašli jsme shodu s k a jsme v listu
  else if x.leaf 
    return nil
  
  else b-tree-search(x.children[i], k)

Vložení prvku (dvoufázově) -

  1. Uzel, kam budeme vkládat, nalezneme pomocí upravené operace b-tree-search, která vrací pointer na uzel , a index v poli x.keys, na který budeme vkládat.
  2. Vložení provedeme posunutím prvků v tomto poli od indexu doprava a na uvolněné místo zapíšeme .
  3. Položku x.n zvětšíme o
  • Problém:
    • Uzel je již zaplněný - x.n == 2t-1, vložením klíče do x bychom v tomto vrcholu měli 2t klíčů a porušili bychom podmínku z definice B-stromu.
    1. Rozdělením uzlu x na dva uzly, každý s t-1 klíči, a přesunem jednoho klíče do rodiče uzlu x.
    2. Po rozdělení můžeme k vložit do příslušného uzlu.
  • Problém:
    • Rodič uzlu x ovšem může být před přidáním také zaplněn.
    • Před začleněním jej tedy musíme také rozdělit.
    • Tím může vzniknout kaskáda dělení zaplněných uzlů, která může skončit až v kořeni.
    • Pokud je zaplněn kořen, rozdělíme jej a vytvoříme nový kořen, který bude obsahovat jenom jeden klíč.

Vložení prvku (jednofázově) -

  • Idea: Pokud v první fázi vkládání rozdělíme každý zaplněný uzel, na který narazíme (a to těsně před tím, než na něj během vyhledávání přejdeme), tak nikdy nenarazíme na problém přidávání do zaplněného uzlu.
  • Jsme-li během vyhledávání v uzlu , který není zaplněn, a víme, že máme pokračovat s vyhledáváním do jeho potomka , který je zaplněn, pak rozdělíme ještě předtím, než na něj přejdeme. Protože není zaplněn, můžeme klíč, který dělením vznikne, vložit do bez nutnosti tento uzel dělit. Tím zabráníme možné kaskádě dělení uzlů.
  • Pro vkládání máme dvě procedury: tree-insert, která bere jako argument strom a vypořádá se se situací, kdy je kořen zaplněný. Druhá procedura, tree-insert-nonfull již bere jako argument uzel: to je kořen podstromu, do kterého vkládáme. O něm předpokládáme, že není zaplněný

Mazání prvku (dvoufázové)

  • 1. fáze - vlastní smazání:
    1. Operací tree-search nalezneme uzel a index , tak, že k == x.keys[i]
    2. Pokud je list, provedeme smazání tak, že jej odstraníme z x.keys (posunutí všech prvků na indexech vyšší než o jedna doleva). Podobně upravíme i položky x.data a x.n snížíme o . Uzel pošleme do druhé fáze.)
    3. Pokud je interní uzel, nahradíme x.keys[i], který je určitě neprázný. Největší klíč v tomto stromu je pořádkovým předchůdcem klíče . Klíč se původně nacházel v listu, ze kterého ho odstraníme tak, jako v bodě (včetně toho, že list pošleme do druhé fáze).
  • 2. fáze - úprava počtu klíčů v uzlech:
    1. Je-li kořen, nebo je-li x.n >= t-1, algoritmus končí
    2. Má-li sourozence a y.n > t-1, pak provedeme přelití jednoho klíče (a jeho satelitních dat) mezi a jejich rodičem. Obrázek ukazuje situaci, kdy je levým sourozencem , opačná situace je symetrická. Po přelití algoritmus končí.
    3. Oba sourozenci mají t-1 prvků. Vybereme si jednoho souseda a spojíme ho s uzlem a jedním klíčem z rodiče do nového uzlu s 2 * t - 1 klíči. Na následujícím obrázku je pravým sourozencem (opačná situace je symetrická). Po spojení uzlů pokračujeme novou iterací fáze 2, do které pošleme rodiče uzlu . Z rodiče jsme totiž odebrali jeden klíč a jeho počet klíčů se tak mohl dostat pod t - 1

Předchozí: AVL stromy, operace a jejich složitost Následující: Hashovací tabulky, metody řešení kolizí Celý okruh: 1. Teoretické základy informačních technologií