350 likes | 694 Views
Algorytmy i struktury danych. Drzewa ukorzenione, Drzewa wyszukiwawcze W zbogacone d rzewa. Drzewo ukorzenione. Drzewo - każdy węzeł może mieć 2 lub więcej węzłów potomnych (następników) Drzewo ukorzenione = drzewo z wyróżnionym jednym węzłem – korzeniem
E N D
Algorytmy i struktury danych Drzewa ukorzenione, Drzewa wyszukiwawcze Wzbogacone drzewa
Drzewo ukorzenione • Drzewo - każdy węzeł może mieć 2 lub więcej węzłów potomnych (następników) • Drzewo ukorzenione = drzewo z wyróżnionym jednym węzłem – korzeniem • Drzewo binarne węzeł zawiera dokładnie dwa wskaźniki do węzłów potomnych (oraz ew. do rodzica) class Node:data = Noneparent = Noneleft = Noneright = None
Drzewo binarne root
Breadth First Search BFS(node, key):"funkcja przeglada drzewo wszerz" data = Put(None, node) while data != None :data, node = Get(data) if node == None : continue Process(node) if node.key==key: return nodedata = Put(data, node.left) data = Put(data, node.right) return None
Depth First Search DFS(node, key):"funkcja przeglada drzewo wgłab " data = Push(None, node) while data != None :data, node = Pop(data) if node == None : continue Process(node); if node.key==key: return node data = Push(data, node.left) data = Push(data, node.right) return None
Depth First Search - rekursyjnie DFSRecursive(node, key):"funkcja przeglada drzewo w głab – w.rekurencyjna" if node == None: return None Process(node) if node.key==k: return noderet = DFSRecursive(node.left, key)if ret!=None : return retelse : return DFSRecursive(node.right, key)
Drzewo k-narne • Drzewo k-narne węzeł zawiera co najwyżej k wskaźników do węzłów potomnych (oraz ew. do rodzica) • Drzewo ogólne można reprezentować przy pomocy notacji „na prawo brat, na lewo syn”
Przegladanie drzewa k-narnego class NODE: data = Nonesons = None # = [None, None, None ....] # fragment funkcja przeszukującej k-drzewo #stary kod data = Put(data, node.left); data = Put(data, node.right) # nowy kod for soon in node.sons: data = Put(data, soon)
Breadth First Search def BFSBrotherSon (node, key): "funkcja przeszukuje wszerzdrzewo w postaci BS" data = Put(None, node) while data != None: data, node = Get(data)while node != None:Process(node) if node.key==key: return node data = Put(data, node.left) node = node.right return node
Depth First Search def DFSBrotherSon (node, key): "funkcja przeszukuje wgłąb drzewo w postaci BS" data = Push(None, node) while data != None: data, node = Pop(data)while node != None: Process(node) if node.key==key: return node data = Push(data, node.right) node = node.left return None
Depth First Search - rekursyjnie def DFSBrotherSonRec(node, key): "funkcja przeszukuje rekursyjnie wgłąb drzewo BS" if node == None: return NoneProcess(node)if node.key == key: return node ret = DFSBrotherSonRec(key.left, key) if ret != None : return retelse: return DFSBrotherSonRec(node.right, key)# a BFS ?
Binarne drzewo poszukiwań 2 5 3 3 7 7 6 2 8 6 8 h=2 5 h=4 y=Left(x) y.key<x.key y=Right(x) y.key≥ x. key
Drukowanie drzewa Binarnego // funkcja wypisuje zawartość drzewa w porządku pre-order def BSTPreorderPrint(node) if node != None : print(node.key+" "+node.data)BSTPreorderPrint(node.left) BSTPreorderPrint(node. right) // funkcja wypisuje zawartość drzewa w porządku in-order def BSTInorderPrint(node):if node != None: BSTInorderPrint(node.left) print(node.key+" "+node.data) BSTInorderPrint(node.right)
16 19 10 17 20 5 12 1 6 14 15 13 • h=4 • max=20 • min=1 • 13: 1610 12 14 13
Szukanie w drzewie BST def BSTSearch(node, key):"Funkcja szuka klucza w BST. Może zwrócić None jeśli nie ma klucza k – Wersja rekurencyjna." if node==Noneor node.key==key: return nodeif key < node.key:return BSTSearch(node.left, key)else:return BSTSearch(node.right, key) O(h(T))
Szukanie w drzewie BST def BSTSearch(node, key):"Funkcja szuka klucza w BST. Może zwrócić None jeśli nie ma klucza k – Wersja iteracyjna." while node!=None and node.key!=key: if key<node.key node = node.left else : node = node.rightreturn node O(h(T))
Min/Max w drzewie BST O(h(T)) #rekurencyjna wersja def BSTMinimum(node):if node.left == None:return node else: return BSTMinimum(node.left) #iteracyjna wersja def BSTMinimumIterative(node):while node.left != None:node = node.left return node O(h(T))
Następnik w drzewie BST // f. zwraca None jezeli node.key jest MAX def BSTSuccesor(node): {if p.right != None: return BSTMinimum(p.right) tmp = node.parent while tmp != Noneandnode == tmp.right:node = tmp tmp = tmp.parent return tmp O(h(T))
Wstawianie węzła 16 19 10 17 20 5 12 1 6 14 18 15 13 • Wstawiany węzeł jest zawsze liściem Postępuj tak jak przy szukaniu. Przy napotkaniu NULL wstaw nowy węzeł.
Wstawianie do drzewia BST def BSTInsertNode (root, node):if root == None node.parent = None node.left = None node.right = Nonereturn nodetmp =root# root != Nonewhile tmp!= NULL: parent = tmpif node.key < tmp.key: tmp = tmp.leftelse: tmp = tmp.right node.parent = parent if node.key < parent.key: parent.left = nodeelse:parent.right = nodereturn root O(h(T))
Usuwanie węzła - brak synów 16 16 19 19 10 10 12 12 20 20 5 5 17 17 14 14 1 1 6 6 15 13 13 Usuwany węzeł jest liściem – zastępujemy go przez None
Usuwanie węzła - jeden syn 16 16 19 19 10 10 12 20 20 5 5 17 17 14 14 1 1 6 6 15 15 13 13 Usuwany węzeł ma dokladnie jednego syna – usuwamy go „kompresując gałąź” w drzewie
Usuwanie węzła – dwóch synów x 16 17 19 10 19 10 12 20 5 17 12 20 5 18 1 6 11 18 1 6 11 Usuwany węzeł ma dwóch synów – • usuwamy inny węzeł, który potrafimy (następnik(x) lub poprzednik(x) ) • przepisujemy wartość z usuniętego węzła x
Usuwanie z drzewa BST def BSTDeleteNode(root, node)ret = rootif root.left==Noneorroot.right==None:todel = nodeelse:todel = BSTSuccesor(node)if todel.left!=None: son = todel.leftelse: son = todel.rightif son!=None: son.parent = todel.parent if todel.parent==None: ret=sonelif todel == todel.parent.left: todel.parent.left = son# todel is lsonelse: todel.parent.right = son# todel is rson if todel != nodenode.key = todel.key node.data = todel.data return ret O(h(T))
Złożoność operacji BST • Wszystkie kluczowe operacja na drzewach mają złożoność – odpowiadającą wysokości drzewa. • Istotne jest utrzymanie jak namniejszej wysokości drzewa. • W dowolnym węźle - poddrzewa: lewe i prawe - powinny mieć zbliżony rozmiar (drzewa zrównoważone) O(h(T))
Wzbogacanie struktury • Wybór struktury podstawowej • Określenie dodatkowych informacji • Sprawdzenie czasu niezbędnego do aktualizacji dodatkowych informacji • Zaprojektowanie nowych operacji
Drzewa statystyk pozycyjnych • Dodatkowy element – rozmiar poddrzewa Zastosowania: • Wyznaczanie statystyk pozycyjnych w czasie log(n) • Wyznaczanie pozycji elementu w czasie log(n)
Drzewo statystyk pozycyjnych 21 20 26 40 7 15 12 17 41 47 2 25 4 10 7 18 4 14 21 30 47 5 4 11 2 16 2 23 1 27 2 20 1 28 38 10 16 19 23 46 1 4 2 17 1 7 1 29 1 7 12 20 39 12 1 3 1 def GetSize(node): if node==None: return 0 else: return node.size x.size = GetSize(x.left) + GetSize(x.right) +1
Drzewo przedziałowe W drzewie przechowywane są odcinki [a, b] • Kluczem będzie lewy koniec odcinka tj. a • Dodatkowy atrybut – max dla wszystkich prawych końców (tj. Max = Max(b)) w danym poddrzewie • Aktualizacja max w czasie log(n) Zastosowania: • Poszukiwanie przedziału zawierającego dany punkt Poszukiwanie przedziału mającego niepuste przecięcie z danym odcinkiem
Drzewo przedziałowe def Interval_search(root, a, b ):x = rootwhile (x!=None && ([a,b] [x.a, x.b] == )): if (x.left && x.left.max >= a): x = x.left else: x = x.right return x
Trie – od reTRIEval A I B F A B F I E U Y O R N T S T S R N N R S O R N S T T E U Y D E E T Y R O S D E E T Y R O S M M Kolejne sekwencje mogą być reprezentowane przez: • wierzchołki • krawędzie
Trie – KOMPRESJA WEZLOW S S S S SAMO SAMO S L C O A LOT CH A O L H C M T O Ó H ODY ÓD O M O D T Ó O D L C O D D Y O L H C Y T O Ó H O D T Ó O D Kompresja utrudnia modyfikację drzewa D D Y Y
Drzewo heterogeniczne T TRIE A C NIEC HNĄĆ BST LIA RKA HAWKA HÓRZ MA