1 / 55

Algorytmy i struktury danych

Algorytmy i struktury danych. R ównoważenie drzew Drzewa czerwono-czarne Drzewa kontekstowe B-drzewa. Drzew a zrównoważone. Czas operacji na BST jest proporcjonalny do wysokości drzewa

grover
Download Presentation

Algorytmy i struktury danych

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Algorytmy i struktury danych Równoważenie drzew Drzewa czerwono-czarne Drzewa kontekstowe B-drzewa

  2. Drzewa zrównoważone • Czas operacji na BST jest proporcjonalny do wysokości drzewa • Drzewo doskonale zrównoważone – dla dowolnego wierzchołka rozmiar lewego i prawego poddrzewa różnią się najwyżej o 1. • Drzewo zrównoważone – długość dowolnej scieżki z węzła do liści różni się od wysokości tego węzła najwyżej o 1. • Drzewo w przybliżeniu zrównoważone – długość dowolnej scieżki z węzła do liści różni się od wysokości tego węzła najwyżej 2 razy.

  3. Przykłady drzewzrównoważonych • Drzewa AVL • Drzewa czerwono czarne • B-drzewa

  4. Drzewoczerwono-czarne • Każdy węzeł jest czerwony lub czarny • Każdy NULL jest czarny • Jeżeli węzeł jest czerwony to obaj jego synowie są czarni • Każda ścieżka z ustalonego węzła do liścia-NULL ma tyle samo czarnych węzłów (czarna wysokość)

  5. Drzewo czerwono-czarne (RBT) 30 • Wysokość drzewa RBT  2lg(n+1) 25 41 15 28 35 45 33 37 8 18 27 29 NULL NULL NULL 4 10 36 40 NULL NULL NULL 26 NULL 20 NULL NULL NULL 1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL

  6. Równowaga w RBT Każda ścieżka z ustalonego wezła do liścia-NULL ma tyle samo czarnych węzłów NIE może być węzła, który nie ma obu potomków i w poddrzewie ma czarne węzły NULL NULL

  7. Właściwości RBT Wysokość drzewa RBT jest  2lg(n+1) Search  O(log(n)) Min  O(log(n)) Max  O(log(n)) Succesor  O(log(n)) Predecesor  O(log(n))

  8. Definicja węzła class NODE : data = Nonecolor = BLACKleft = Noneright = Noneparent = none

  9. Operacja rotacji y x x y C A C B A B RightRotate(T,y) LeftRotate(T,x)

  10. LeftRotate y x x y C A C B A B def LeftRotate(root, x): # zmiana na righty = x.right # x.leftif (y==None): returnx.right = y.left# x.rightif (y.left!=None): y.left.parent = x# y.righty.parent = x.parentif x.parent==None:root = yelif x==x.parent.left:x.parent.left = yelse:x.parent.right = yy.left = x#y.rightx.parent = yreturn root

  11. Wstawianie węzła Nie narusza długości czarnych ścieżek Narusza długość czarnych ścieżek Ale może naruszyć zasadę 3

  12. Wstawianie węzła • Wstaw węzeł x do drzewa (liść) • Pokoloruj x na czerwono • Uporządkuj drzewo (naruszona może być własność 3):

  13. Porządkowanie RBT po INS - 1 15 20 4 8 25 1 Przypadek 1 15 6 10 stryj 20 4 5 x nowy x 8 25 1 6 10 5 Jeżeli stryj(x) jest czerwony przemaluj wierzchołki dziadek(x), ojciec(x), stryj(x) wznów operację od dziadka, tj. podstaw x = dziadek(x)

  14. Porządkowanie RBT po INS - 2 15 stryj 20 15 4 stryj 25 14 x 8 nowy x 8 1 15 4 10 6 10 Przypadek 2 6 1 5 5 Jeżeli x jest synem z tej strony co stryj(x) (kierunek ) Przyjmij x = ojciec(x) obróć drzewo w przeciwną, tj. ’ względem nowego x Uwaga po tej operacji x znajduje się po przeciwnej stronie swego ojca niż stryj swojego

  15. Porządkowanie RBT po INS - 3 11 stryj 14 7 x 15 2 8 7 x 11 14 5 2 1 8 5 15 Przypadek 3 4 1 4 Jeżeli x jest synem z przeciwnej strony niż stryj(x) Przemaluj wierzchołki ojciec(x) i dziadek(x), a następnie, obróć drzewo względem dziadek(x) w stronę stryja tj. 

  16. Porządkowanie drzewa po INS Z: korzeń drzewa jest czarny(czemunie ?) • Dopóki ojciec(x) jest czerwony i nie doszliśmy do korzenia Jeżeli stryj(x) jest czerwony • przemaluj wierzchołki dziadek(x), ojciec(x), stryj(x) i wznów operację od dziadka, tj. podstaw x = dziadek(x) W przeciwnym razie Jeżeli x jest synem z tej strony co stryj(x) (kierunek ) • Przyjmij x = ojciec(x) obróć drzewo w przeciwną tj ’ względem x (nowego) • Przemaluj ojciec(x) i dziadek(x), a następnie, obróć drzewo względem dziadek(x) w stronę stryja tj.  • Upewnij się czy korzeń drzewa jest dalej czarny

  17. Wstawianie węzła – implem. def RBTInsert(root, x):Insert(root,x)x.color=RED#zalozenie: root.color == BLACKwhilex != root and x.parent.color==RED:if x.parent == x.parent.parent.left: #Porządkowanie dla ojca po lewejelse: #Porządkowanie dla ojca po prawejroot.color = BLACKreturn root

  18. Porządkowanie dla ojca po lewej # Porządkowanie dla ojca po lewej uncle = x.parent.parent.right if GetColor(uncle) == RED :x.parent.color = BLACK# przypadek 1uncle.color = BLACKx.parent.parent = REDx = x.parent.parentelse:if x == x.parent.right:x = x.parent# przypadek 2root = LeftRotate(root, x)x.parent.color = BLACK# przypadek 3x.parent.parent.color = RED root = RightRotate(root, x.parent.parent)

  19. Pobieranie koloru - NULL def GetColor(node):if node!=None:return node.colorelse:return BLACK

  20. Usuwanie węzła NULL NULL Przenosimy nadmiarowy kolor czarny na syna usuwanego wierzchołka Problem: wierzchołek mógł nie mieć syna Czy mogl mieć czarnego syna(ów) ?

  21. Usuwanie węzła • Usuńwęzeł podobnie jak dla zwykłego drzewa • Jeżeli usuwany wierzchołek był koloru czarnego należy wykonac porządkowanie drzewa (naruszona może być własność 3 lub 4):

  22. Porządkowanie RBT po DEL - 1 2 x brat 10 1 B A A 7 15 D C E F Przypadek 1 10 2 15 brat (nowy) x 7 1 E F Jeżeli brat jest czerwony przemaluj wierzchołki brat, ojciec(x), obróć drzewo wokół wierzchołka ojciec(x) w kierunku syna x (tj. )i zaktualizuj brat Uwaga: po tym kroku brata jest czarny B C A A D

  23. Porządkowanie RBT po DEL - 2 E E C C D F F D 2 x brother 7 1 A B Przypadek 2 5 9 nowe x 2 7 1 A B 5 9 Jeżeli obaj synowie brat-asą czarni przemaluj brat-a i ustaw rozpocznij od ojciec(x)

  24. Porządkowanie RBT po DEL - 3 C E E F D F 2 x brother 7 1 Przypadek 3 2 A B x 5 9 brother (nowy) 5 1 A C B 7 D 9 Jeżeli przynajmniej jeden syn brat-ajest czerwony jeżeli dalszy syn brat-a (w kierunku ’) jest czarny drugiego syna brat-a pomaluj na czarno, brat-a na czerwono i obróć drzewo wokoł wierzchołka brat w kierunku ’, zaktualizuj brat Uwaga: po kroku trzecim dalszy syn brata bedzie czerwony

  25. Porządkowanie RBT po DEL - 4 E E C C D F F D 2 x brother 5 1 5 A B Przypadek 4 3 7 2 7 x 3 1 A B STOP: np x = proot Jeżeli dalszy syn brat-ajest czerwony przemaluj brat-a na kolor taki jak ojciec(x), wierzchołki ojciec(x) oraz dalszego syna brat-a (w kierunku ’) na czarno, obróć drzewo wokół ojca w kierunku x (tj. )

  26. Porządkowanie drzewa po DEL Dopóki ojciec(x) jest czarny i nie doszliśmy do korzenia if brat jest czerwony : • przemaluj wierzchołki brat, ojciec(x), obróć drzewo wokół wierzchołka ojciec(x) w kierunku syna x (tj. )i zaktualizuj brat elif obaj synowie brat-asą czarni : • przemaluj brat-a i ustaw rozpocznij od ojciec(x) else : • if dalszy syn brat-a (w kierunku ’) jest czarny : drugiego syna brat-a pomaluj na czarno, brat-a na czerwono obróć drzewo wokoł wierzchołka brat w kierunku ’, zaktualizuj brat • przemaluj brat-a na kolor taki jak ojciec(x), przemaluj wierzchołki ojciec(x) oraz dalszego syna brat-a (w kierunku ’) na czarno, obróć drzewo wokół ojca w kierunku x (tj. )zakończ porządkowanie (np. podstaw x = proot)

  27. Uwagi implementacyjne Aby uniknąćw kodzie sprawdzania warunków czy syn(owie) != None przed pobraniem koloru / sprawdzeniem typu / odwolaniem sie do ojca można: • dodać funkcje realizujące odpowiednie testy (por. GetColor) • dodać wartownika np NIL – specjalny węzeł który ma wszystkie wskaźniki == None i na który pokazują wszystkie wskaźniki dawniej równe None Przypisanie: son.parent = todel.parent można wykonać bezwarunkowo (syn moze być ew. wartownikiem, ale istnieje) - w takim przypadku NIL.parent == todel.parent.

  28. Usuwanie węzła – implem. def RBTDelete(root, p): if p.left==Noneorp.right==None:todel = p else todel = BSTSuccesor(p) if todel.left != NIL: son = todel.leftelse: son = todel.rightson.parent = todel.parent if todel.parent==None: root = son elif todel == todel.parent.left: todel.parent.left = sonelse: todel.parent.right = son if todel != p: p.key = todel.key p.data = todel.dataif todel.color ==BLACK: root = RBTDeleteFix(root, son)return root

  29. DEL - Porządkowanie węzłów def RBTDeleteFix (root, x):whilex != root and x.color == BLACK:if x == x.parent.left: # porządkowanie dla lewego węzła else: # porządkowanie dla prawego węzłax.color = BLACK

  30. DEL - Porządkowanie lewego węzła brother = x.parent.right if brother.color == RED: brother.color = BLACK# przypadek 1x.parent.color = RED root = LeftRotate (root, x.parent) brother = x.parent.right elif (brother.left.color == BLACK and\ brother.right.color == BLACK: brother.color = RED# przypadek 2x = x.parent else: ...

  31. Porządkowanie lewego węzła cd else:if brother.right.color == BLACK: brother.left.color = BLACK#przypadek 3 brother.color = RED root = RightRotate(root, brother) brother = x.parent.rightbrother.color = x.parent.color#przypadek 4x.parent.color = BLACKbrother.right.color = BLACKroot = LeftRotate (root, x.parent)x = root

  32. RBT wzbogacone o statystyki poz. Aktualizacja rozmiarów: def LeftRotate (root, x): ..... y.size = getsize(y); x.size = getsize(x.left) + getsize(x.right) +1return root 93 19 y 42 19 x y T=RightRotate(T,y) x 42 11 7 93 12 6 T=LeftRotate(T,x) 4 6 7 4

  33. B-drzewo . M . n.keys[1] n.keys[0] . D . H . . Q . T . X . n.sons[0] n.sons[2] n.sons[1] B C F G J K L N P R S V W Y Z • Wszystkie klucze dla i-tego syna jego potomków są wieksze lib równe od i-tego klucza i mniejsze lub równe od i+1 • Węzeł o i synach ma i-1 kluczy • Wezły różne od korzenia zawierają co najmniej T-1 kluczy (stąd węzły wewnętrzne maja conajmniej t synów) • Węzły zawierają conajwyżej 2T-1 kluczy (stąd węzły wewnętrzne maja conajwyżej 2T synów -> węzły pełne)

  34. Minimalne B-drzewo o h=3 T - 1 T - 1 T - 1 T - 1 T - 1 T - 1 T T T T T T root 1 2 2t 2t2 1 T - 1 T - 1 T - 1 T - 1 T - 1 T - 1 T - 1 T - 1 Dla T = 2 otrzymujemy tzw. 2-3-4 drzewo

  35. Właściwości B-drzewa • B-drzewo jest zrównoważone • Zmienna liczba kluczy i synów • Wszystkie liście są na tej samej głębokości • Mała głębokość drzewa • Zaprojektowane do minimalizacja dostepów np. do dysku – korzeń wczytuje się pamięci od razu

  36. Definicja węzła T = 5 class BNODE: isLeaf=truecntKey=0keys = Array(2*T-1, None) sons = Array(2*T, None) #pozycja na dysku biezacego wezlathisNodeDiscPos = None#pozycje na dysku dla danych odpowiadających#poszczegolnym kluczomdataDiscPos = Array(2*T-1, None) def Array(size, initVal=None): return map(lambda x: initVal, range(0,size)) class DISCPOS:...

  37. FunkcjePomocnicze def LoadNode(nodeDiscPos) # alokacja w pamięci i odczyt def WriteNodeToDisc(node) # zapis na dysk pod pode. thisNodeDiscPos AllocateNode() # alokacja w pamięci i na dysku, # zapis struktury na dysk p = BNODE() #p.isLeaf = true, p.cntKey = 0 p.thisNodeDiscPos = AllocateSpaceOnDisc() WriteNodeToDisc(p) return p

  38. Wyszukiwanie w B-drzewie BTreeFind(p,k): if p_zawiera_szukany_klucz k: return p elif p_jest_liściem: return None else: # p nie jest liściem i nie zawiera k s = wytypuj_poddrzewo_p_które_może_zwierać_k ptmp = LoadNode(s) ret = BTreeFind(ptmp,k) #zadbaj o zwolnienie ptmp jeśli ret!=ptmp return ret

  39. Wyszukiwanie w B-drzewie BTreeFind(p,k): for i in range(0, p.cntKey): if k<=p.keys[i]:break if p.keys[i] == k: return p if p.isLeaf: return None ptmp = LoadNode(p.sons[i]) ret = BTreeFind(ptmp, k) return ret

  40. Rozbijanie węzła T = 4 keys[i-1] keys[i] p N . W sons[i] w . P . Q . R . S . T . U . V . keys[i] keys[i-1] keys[i+] p N . S . W sons[i] sons[i+1] w y . P . Q . R . . T . U . V .

  41. Rozbijanie korzenia T=4 w . P . Q . R . S . T . U . V . root keys[0] p . S . sons[0] sons[1] w y . P . Q . R . . T . U . V .

  42. Rozbijanie węzła w B-drzewie • rozbijamy pełen węzeł w będący i-tym synem węzła p • środkowy z 2*T-1 kluczy w w wstawiamy do węzła p (przed element na pozycji i) • wskaźnik na nowy węzeł z wstawiamy do węzła p (przed element na pozycji i) • T-1 kluczy z w przepisujemy do z • T wskaźników z w przepisujemy do z • zwracamy nowy węzeł (odbiorca powinien go zwolnić)

  43. Rozbijanie węzła w B-drzewie BTreeSplit(p, i, w):#Zalozenie: p!=w jeśli mamy rozbic korzeń najpierw #należy dodac nowy wezel(powyzej korzenia)z = AllocateNode()z.isLeaf = w.isLeafz.cntKeys, w.cntKeys = T-1, T-1 for j in range(p.cntKey-1,i,-1): p.keys[j]=p.keys[j-1] #p.data[j]=p.data[j-1] for j in range(p.cntKey, i,-1): p.sons[j]=p.sons[j-1] p.keys[i] = w.keys[T-1] #p.data[i]=w.data[T-1] p.sons[i] = zp.cntSons = p.cntSons +1for j in range(0, T-1): z.keys[j] = w.keys[T+j] #z.data[j]=w.data[T-1+j]for i in range(0,T): z.sons[j] = w.sons[T+j] WriteNodeToDisc(p) WriteNodeToDisc(w) WriteNodeToDisc(z)return z

  44. T=3 . G . M . P . X . A C D E J K N O R S T U V Y Z +B . G . M . P . X . A B C D E J K N O R S T U V Y Z +Q . G . M . P . T . X . A B C D E J K N O Q R S U V Y Z

  45. T=3 . G . M . P . T . X . A B C D E J K N O Q R S U V Y Z +L . P . . G . M . . T . X . A B C D E J K L N O Q R S U V Y Z +F . P . . C . G . M . . T . X . A B D E F J K L N O Q R S U V Y Z

  46. Wstawianie klucza do B-drzewa (1) if korzeń jest pełny:dodajemy nowy korzeń i rozbijamy dotychczasowy na dwa (2) Wybieramy syna p mogącego zawierać nowy klucz k if p jest pełen: rozbijamy p na dwa p i q Wybieramy odpowiedni z wezłów p lub q if wybrany wezeł jest liściem: dodajemy do niego klucz k else: dla wybranego węzła wołamy rekurencyjnie (2)

  47. Wstawianie klucza do B-drzewa def BTreeInsert(root, key, data):if root.cntSons == 2*T-1: BNODE * s = AllocateNode() s.sons[0] = r s.cntKeys = 0 s.isLeaf = falseBTreeSplit(s, 0, root) root = s BTTreeInsert_NonFul(root, k, data)return root

  48. Wstawianie klucza do B-drzewa def BTreeInsert_NonFull(x, k, data):if (x.isLeaf): BTreeInsert_ToLeaf(x, k, data)else: BTreeInsert_ToNonLeaf(x, k, data) def BTreeInsert_ToNonLeaf(x, k, data) :for i in range(x.cntSons-1,-1,-1): if k>=x.keys[i]: breaki=i+1ptmp = LoadNode(x->sons[i]) if ptmp.cntSons == 2*T-1: q = BTreeSplit(x, i, ptmp) if k>x.keys[i]: ptmp=qBTreeInsert_NonFull(ptmp, k, data)

  49. Wstawianie klucza do B-drzewa def BTreeInsert_ToLeaf(x, k, data):for i in range(x.cntSons-1, -1, -1): if k<x.keys[i]: x.key[i+1] = x.key[i] else: breakx.key[i+1] = kx.data[i+1] = AllocateDataOnDisc(data)x.cntKeys = x.cntKeys +1 WriteNode(x)

  50. T=3 . P . . C . G . M . . T . X . A B D E F J K L N O Q R S U V Y Z -F . P . . C . G . M . . T . X . A B D E J K L N O Q R S U V Y Z -M . P . . C . G . L . . T . X . A B D E J K N O Q R S U V Y Z

More Related