Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i...

27
Podstawowe struktury danych Listy Lista to skończony ciąg elementów: q=[x 1 , x 2 , ... , x n ]. Skrajne elementy x 1 i x n nazywamy końcami listy, a wielkość |q| = n długością (rozmiarem) listy. Szczególnym przypadkiem jest lista pusta: q = [ ]. Podstawowe abstrakcyjne operacje na listach q =[x 1 , x 2 , ... , x n ] i r =[y 1 , y 2 , ... , y m ] dla 1 i j n to: dostęp do elementu listy - q[i] = x i ; podlista - q[i..j] = [x i , x i+1 , ... , x j ] ; złożenie - q&r = [x 1 , ... , x n , y 1 , ... ,y m ] ; Na podstawie operacji podstawowych można zdefiniować inne operacje, np. wstawianie elementu x za element x i , na liście q: q[1..i] & [x] & q[i+1 .. |q| ]. W operacjach na listach ograniczamy się zwykle do zmian ich końców: a) front(q) = q[1] - pobierz lewy koniec listy b) push(q,x) = [x]&q - wstaw element na lewy koniec c) pop(q) =q[2..|q| ] - usuń bieżący lewy koniec d) rear(q) = q[|q|] - pobierz prawy koniec listy e) inject(q,x) =q&[x] -wstaw element na prawy koniec f) eject(q) =q[1..|q|-1 ] - usuń bieżący prawy koniec

Transcript of Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i...

Page 1: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Podstawowe struktury danych

Listy

Lista to skończony ciąg elementów: q=[x1, x2, ... , xn ].Skrajne elementy x1 i xn nazywamy końcami listy, a wielkość |q| = n długością (rozmiarem) listy.Szczególnym przypadkiem jest lista pusta: q = [ ].

Podstawowe abstrakcyjne operacje na listach q =[x1, x2, ... , xn ] i r =[y1, y2, ... , ym ] dla 1 ≤ i ≤ j ≤ n to:• dostęp do elementu listy - q[i] = xi ;• podlista - q[i..j] = [xi , xi+1 , ... , xj ] ;• złożenie - q&r = [x1 , ... , xn , y1, ... ,ym ] ;

Na podstawie operacji podstawowych można zdefiniować inne operacje, np. wstawianie elementu x za element x i, na liście q: q[1..i] & [x] & q[i+1 .. |q| ].

W operacjach na listach ograniczamy się zwykle do zmian ich końców:a) front(q) = q[1] - pobierz lewy koniec listy

b) push(q,x) = [x]&q - wstaw element na lewy koniec

c) pop(q) =q[2..|q| ] - usuń bieżący lewy koniec

d) rear(q) = q[|q|] - pobierz prawy koniec listy

e) inject(q,x) =q&[x] -wstaw element na prawy koniec

f) eject(q) =q[1..|q|-1 ] - usuń bieżący prawy koniec

Page 2: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

W zależności od możliwości wykonania różnych operacji wyróżniemy:• kolejkę podwójną - wszystkie sześć operacji• stos - tylko operacje front, push, pop• kolejkę - tylko operacje front, pop , inject

Dwie podstawowe implementacje (reprezentacje) listy q =[x1, x2, ... , xn ] to:• tablicowa - q[i] = xi , gdzie 1≤ i ≤ n,• dowiązaniowa

W implementacjach pojedynczej liniowej i podwójnej liniowej dowiązanie prowadzące do listy wskazuje na pierwszy element na liście.

W implementacji pojedynczej cyklicznej i podwójnej cyklicznej dowiązanie prowadzące do listy wskazuje na element ostatni.

Page 3: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Aby dowiązana struktura nigdy nie była pusta dodaje się na początku listy element pusty zwany GŁOWĄ lub WARTOWNIKIEM listy.

Operacje na listach o stałej złożoności czasowej:a) w implementacji pojedynczej liniowej: operacje stosu,

wstawianie jednego elementu za drugi, usuwanie następnego elementu,

b) w implementacji pojedynczej cyklicznej: te co w a) oraz złożenie i operacje rear i inject,

c) w implementacji podwójnej cyklicznej: te co w b) oraz eject, wstawianie jednego elementu przed drugim, wstawianie danego elementu, odwracanie listy.

LISTY JEDNOKIERUNKOWE

Lista jednokierunkowa jest oszczędną pamięciowo strukturą danych, pozwalającą grupować dowolną - ograniczoną tylko ilością dostępnej pamięci - liczbę elementów: liczb, znaków, rekordów.

Do budowy listy używane są dwa typy rekordów:- informacyjny - wskaźniki, dowiązania do początku listy

(głowa) i końca listy (ogon)- robocze - pole wartości i wskaźnik do następnego

elementu listy

Dzięki rekordowi informacyjnemu mamy ciągły dostęp do niektórych operacji, np. dołączanie elementu na koniec listy.

Głowa, ogon i następny to wskaźniki, wartość to dowolna wielkość (znanego typu).

Page 4: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Wskaźniki NULL oznaczają adresy pamięci pod którymi nie ma żadnej zmiennej.

Przykład listy jednokierunkowej:

Page 5: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Pola głowa i ogon pozwalają na przeglądanie elementów listy i dołączanie nowych elementów.

Przykład (pseudokod) przeglądania elementów listy:_________________________________________________adres_tmp=info.głowa;while (adres_tmp <> NULL ) { if(adres_tmp.wartość == x) { Wypisz "Znalazłem szukany element" opuść procedurę } else adres_tmp=adres_tmp.następny }Wypisz "Nie znalazłem elementu" _________________________________________

Dokładanie nowych elementów (dwa podejścia):1)potraktowanie listy jak worek nie-uporządkowanych elementów i umieszczanie nowych elementów na początku

Page 6: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

1) dokładanie elementów we właściwym ustalonym przez użytkownika porządku (całość listy musi być widziana jako posortowana)

Możliwe są trzy przypadki:

a) wstawiamy element na początek listy

b)wstawiamy element na koniec listy

c) wstawiamy element gdzieś w środku

W każdym z przypadków musimy zapamietywać dwa wskaźniki - przed który element wstawić i po którym mamy to zrobić.

Page 7: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Podobnie postępujemy przy fuzji (łączeniu) list tak by wypadkowa lista pozostała uporządkowana.

Podsumowując wady i zalety list jednokierunkowych:

Wady Zaletynienaturalny dostęp do

elementówmałe zużycie pamięci

niełatwe sortowanie elastyczność

Lista w której elementy są już na samym początku wstawiane w określonym porządku, służy obok gromadzenia danych, także do ich porządkowania.

W sytuacji, gdy jest tylko jedno kryterium sortowania struktura działa bardzo dobrze "sama" dbając o sortowanie.

Page 8: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Dla kilku kryteriów sortowania należy wprowadzić obok listy danych, także kilka list z wskaźnikami do danych - list tych powinno byś tyle ile kryteriów sortowania.Sortowanie w takich wypadku polega na porządkowaniu wskaźników bez ruszania listy danych.

Nieposortowaną listę DANE można uporządkować według trzech kryteriów:

Page 9: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

- imienia i nazwiska (Adam Fuks, Jan Kowalski, Michał Zaremba)

- kodów ( 30, 34, 37)- kwot ( 1200, 2000, 3000 )

Tablicowa implemantacja list jest niezwykle prosta jeśli umówimy się, że i-temu indeksowi tablicy odpowiada i-ty element listy. Wymagana jest dodatkowa informacja wskazująca jak wiele elementów liczy lista (jak duża musi być tablica).

Wadą jest marnotrawstwo pamięci bo najczęściej przydzielamy na tablicę większy obszar pamięci niż to zwykle potrzeba.

Operacje na listach są w implementacji tablicowej proste:1) front(q) , x=q[1] - pobierz lewy koniec listy2) push(q,x) - przesuń wszystkie elementy tablicy o jeden w

prawo i q[1]=x - wstaw element na lewy koniec3) pop(q), przesuń wszystkie elementy tablicy poza

pierwszym o jeden w lewo - usuń bieżący lewy koniec 4) rear(q), x=q[n] - pobierz prawy koniec listy5) inject(q,x), q[n+1]=x -wstaw element na prawy koniec 6) eject(q), n=n-1 - usuń bieżący prawy koniec

Page 10: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Dodatkowo:

A) usunięcie k-tego elementu to - przesunąć w lewo elementy tablicy q[k+1]...q[n], n=n-1

B) wstawienie elementu na pozycję k to - przesunąć w prawo elementy tablicy q[k]...q[n], n=n+1

LISTY DWUKIERUNKOWE

Listy jednokierunkowe są wygodne i zajmują mało pamięci. Operacje na nich zajmują dużo czasu.

W liście dwukierunkowej komórka robocza zawiera wskaźniki do elementów: poprzedniego i następnego .• pierwsza komórka na liście nie posiada swojego

poprzednika (pole poprzedni zawiera NULL wskaźnik pokazujący pusty element pamięci)

• ostatnia komórka na liście nie posiada swojego następnika (pole następny zawiera NULL wskaźnik pokazujący pusty element pamięci)

Lista dwukierunkowa jest kosztowna jeśli chodzi o pamięć, ale wygodna gdy chodzi o szybkość.

Page 11: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Usunięcie elementu listy dwukierunkowej:

Lista cykliczna jest zamknięta w pierścień , wskaźnik „ostatniego” elementu wskazuje na „pierwszy” element.

Elementy „pierwszy” i „ostatni” są umowne.

STOSY

Stos jest struktura danych, do której dostęp jest możliwy tylko od strony tzw. wierzchołka, czyli pierwszego wolnego miejsca znajdującego się na nim.

Funkcje odkładania elementu X na stos ( push(X) ) i pobieranie go ze stosu ( pop(X) ) można opisać symbolicznie (wraz z kodem błędu s wprowadzonym przez użytkownika):

Page 12: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Tablicowa implementacja stosu wygląda analogicznie jak dla listy, ale z dostępnymi jedynie operacjami front, push, pop.

Grafy

Wprowadzenie do teorii grafów.

Przykład

Page 13: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Ważony graf skierowany.Kółka – wierzchołki grafu.Linie łączące wierzchołki - krawędzie grafu.

Wszystkie krawędzie posiadają przypisany kierunek– graf skierowany (digraf).W digrafie mogą istnieć dwie krawędzie między dwoma wierzchołkami, każda biegnąca w innym kierunku.

Jeżeli krawędzie posiadają związane ze sobą wartości, są one nazywane wagami (nieujemne). Graf jest zwany grafem ważonym.

Droga w grafie ważonym to sekwencja wierzchołków, taka że istnieje krawędź z każdego wierzchołka do jego następnika – [ν1, ν4, ν3] jest drogą, [ν3, ν4, ν1] nie jest drogą.

Droga jest nazywana prostą, jeżeli nie przechodzi dwa razy przez ten sam wierzchołek. Droga prosta nigdy nie zawiera pod-drogi, która byłaby cykliczna.

Długością drogi w grafie skierowanym jest suma wag krawędzi należących do drogi.

Droga z wierzchołka do niego samego – cykl. Graf zawierający cykl jest grafem cyklicznym, gdy nie zawiera cyklu jest grafem acyklicznym.

Najczęstsze zadanie dla grafów to znalezienie najkrótszych dróg z każdego wierzchołka do wszystkich innych wierzchołków. Najkrótsza droga musi być drogą prostą.

Page 14: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Grafy i drzewa

Załóżmy, że planista przestrzenny chce połączyć określone miasta drogami w taki sposób, aby było możliwe dojechanie z dowolnego z tych miast do dowolnego innego. Dążymy do zbudowania najkrótszej sieci dróg. Do rozwiązania tego problemu niezbędne jest poznanie zagadnień z zakresu teorii grafów.

Przypomnijmy, że graf jest nieskierowany, gdy jego krawędzie nie posiadają kierunku. Mówimy wówczas po prostu, że krawędź jest między dwoma wierzchołkami.Droga w grafie nieskierowanym jest sekwencją wierzchołków, taką że każdy wierzchołek i jego następnik łączy krawędź. Krawędzie nie mają kierunku, więc droga z wierzchołka u do wierzchołka ν istnieje wtedy i tylko wtedy, gdy istnieje droga z ν do u.

Graf nieskierowany jest nazywany spójnym , kiedy między każdą parą wierzchołków istnieje droga. Grafy z rysunku poniżej są spójne.

Page 15: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

W grafie nieskierowanym droga wiodąca z wierzchołka do niego samego, zawierająca co najmniej 3 wierzchołki, wśród których wszystkie wierzchołki pośrednie są różne, jest nazywana cyklem prostym. Graf nieskierowany nie zawierający żadnych cykli prostych jest określany mianem acyklicznego (grafy (a), (b) są cykliczne).

Drzewo jest acyklicznym spójnym grafem nieskierowanym (grafy (c), (d) są drzewami). Funkcjonuje też pojęcie drzewo korzeniowe – to drzewo posiadające jeden wierzchołek, określony jako korzeń (jest to inna klasa drzew niż te rozpatrywane tutaj).

Szerokie zastosowanie ma problem usuwania krawędzi ze spójnego, ważonego grafu nieskierowanego G w celu utworzenia takiego pod-grafu, że wszystkie wierzchołki pozostają połączone, a suma ich wag jest najmniejsza. Pod-graf o minimalnej wadze musi być drzewem, ponieważ gdyby tak nie było, zawierałby cykl prosty, więc usunięcie krawędzi tego cyklu prowadziłoby do grafu o mniejszej wadze.

Drzewo rozpinające grafu G to spójny pod-graf, który zawiera wszystkie wierzchołki należące do G i jest drzewem ( (c) i (d) są drzewami rozpinającymi). Spójny pod-graf o minimalnej wadze musi być drzewem rozpinającym, ale nie każde drzewo rozpinające ma minimalną wagę. Algorytm rozwiązujący przedstawiony wcześniej problem musi tworzyć drzewo rozpinające o minimalnej wadze. Takie drzewo nosi nazwę minimalnego drzewa rozpinającego ((d) jest takim drzewem).

Znalezienie minimalnego drzewa rozpinającego metodą siłową wymaga czasu gorszego niż wykładniczy. Chcemy

Page 16: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

rozwiązać to bardziej wydajnie wykorzystując podejście zachłanne.__________________________________________________DefinicjaGraf nieskierowany G składa się ze skończonego zbioru V wierzchołków oraz zbioru E par wierzchołków ze zbioru V czyli krawędzi grafu. Graf G oznaczamy: G = ( V, E )________________________________________________Dla grafu (a): V = {ν1,ν2,ν3,ν4,ν5} E = {(ν1,ν2),(ν1,ν3),(ν2,ν3),(ν2,ν4),(ν3,ν4),(ν3,ν5),(ν4,ν5)}

Drzewo rozpinające T dla grafu G zawiera te same wierzchołki V , co graf G , jednak zbiór krawędzi drzewa T jest podzbiorem F zbioru E . Drzewo rozpinające możemy oznaczyć jako T = ( V , F ). Problem polega na znalezieniu podzbioru F zbioru E , takiego aby T = ( V , F ) było minimalnym drzewem rozpinającym grafu G .

Wysokopoziomowy algorytm zachłanny realizujący to zadanie mógłby wyglądać:

F=∅; //Inicjalizacja zbioru krawędziwhile (realizacja nie została rozwiązana) { wybierz krawedz zgodnie z warunkiem optymalnym lokalnie; // procedura wyboru

if(dodanie krawędzi do F nie powoduje powstania cyklu) // spr. wykonalnosci dodaj ją;

if(T=(V,F) jest drzewem rozpinajacym) realizacja jest rozwiazana;}

Page 17: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Oczywiście „warunek optymalny lokalnie” może być inny w różnych problemach i w rożnych algorytmach rozwiązania.

Dwa najbardziej znane algorytmy realizujące to zadanie to algorytm Prima i algorytm Kruskala.

Drzewa wyszukiwania binarnego ( i ich optymalizacja)

Opracowujemy algorytm określania optymalnego sposobu zorganizowania zbioru elementów w postaci drzewa wyszukiwania binarnego.

Dla każdego wierzchołka w drzewie binarnym poddrzewo, którego korzeniem jest lewy (prawy) potomek tego wierzchołka, nosi nazwę lewego (prawego) pod-drzewa wierzchołka.

Lewe (prawe) pod-drzewo korzenia drzewa nazywamy lewym (prawym) pod-drzewem drzewa.

Drzewo wyszukiwania binarnego.

Drzewo wyszukiwania binarnego jest binarnym drzewem elementów (kluczy) pochodzących ze zbioru uporządkowa-nego. Najprostsze drzewo wyszukiwania binarnego spełnia warunki:• Każdy wierzchołek zawiera jeden klucz.

• Każdy klucz w lewym poddrzewie danego wierzchołka jest mniejszy lub równy kluczowi tego wierzchołka.

Page 18: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

• Klucze znajdujące się w prawym pod-drzewie danego wierzchołka są większe lub równe kluczowi tego wierzchołka.

Przykład.

Dwa drzewa o tych samych kluczach. W lewym drzewie prawe pod-drzewo wierzchołka Rudolf zawiera klucze (imiona) Tomasz, Urszula, Waldemar wszystkie większe od Rudolf zgodnie z porządkiem alfabetycznym.

Zakładamy, że klucze są unikatowe.

Głębokość wierzchołka w drzewie jest liczbą krawędzi w unikatowej drodze, wiodącej od korzenia do tego wierzchołka, inaczej zwana poziomem wierzchołka w drzewie.

Głębokość drzewa to maksymalna głębokość wszystkich wierzchołków (w przykładzie - drzewo po lewej głębokość 3, po prawej głębokość 2)

Page 19: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Drzewo nazywane jest zrównoważonym, jeżeli głębokość dwóch pod-drzew każdego wierzchołka nigdy nie różni się o więcej niż 1 (w przykładzie – lewe drzewo nie jest zrównoważone, prawe jest zrównoważone).

Zwykle drzewo wyszukiwania binarnego zawiera pozycje, które są pobierane zgodnie z wartościami kluczy. Celem jest takie zorganizowanie kluczy w drzewie wyszukiwania binarnego, aby średni czas zlokalizowania klucza był minimalny. Drzewo zorganizowane w ten sposób jest nazywane optymalnym.

Jeżeli wszystkie klucze charakteryzuje to samo prawdopodobieństwo zostania kluczem wyszukiwania, to drzewo z przykładu (prawe) jest optymalne.

Weźmy przypadek, w którym wiadomo, że klucz wyszukiwania występuje w drzewie. Aby zminimalizować średni czas wyszukiwania musimy określić złożoność czasową operacji lokalizowania klucza.

________________________________________________________Algorytm wyszukiwania klucza w drzewie wyszukiwania binarnego

Wykorzystujemy strukturę danych: struct nodetype { keytype key; nodetype* left; nodetype* right; }; typedef nodetype* node_pointer;

Page 20: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Zmienna typu node_pointer jest wskaźnikiem do rekordu typu nodetype.

Problem: określić wierzchołek zawierający klucz w drzewie wyszukiwania binarnego, zakładając że taki występuje w drzewie.

Dane: wskaźnik tree do drzewa wyszukiwania binarnego oraz klucz keyin.

Wynik: wskaźnik p do wierzchołka zawierającego klucz.

void search(node_pointer tree, keytype keyin, node_pointer p){ bool found;

p = tree; found = false; while (!found) if (p->key == keyin) found = true; else if (keyin < p->key) p = p->left; else p = p->right;}

Liczbę porównań wykonywanych przez procedurę search w celu zlokalizowania klucza możemy nazwać czasem wyszukiwania . Chcemy znaleźć drzewo, dla którego średni czas wyszukiwania jest najmniejszy.

Zakładając, że w każdym przebiegu pętli while wykonywane jest tylko jedno porównanie możemy napisać : czas wyszukiwania = głębokość(key) + 1 Przykładowo (lewe poddrzewo): czas wyszukiwania = głębokość(Urszula) + 1 = 2+1 = 3

Page 21: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Niech Key1, Key2, …, Keyn będą n uporządkowanymi kluczami oraz pi będzie prawdopodobieństwem tego, że Keyi jest kluczem wyszukiwania. Jeżeli ci oznacza liczbę porównań koniecznych do znalezienia klucza Keyi w danym drzewie, to: n

średni czas wyszukiwania = Σci pi

i=1

Jest to wartość która trzeba zminimalizować.

Przykład.

Mamy 5 różnych drzew dla n = 3. Wartości kluczy nie są istotne.

Page 22: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Jeżeli: p1 = 0.7 , p2 = 0.2 oraz p3 = 0.1

to średnie czasy wyszukiwania dla drzew wynoszą :1) 3*(0.7) + 2*(0.2) + 1*(0.1) = 2.62) 2*(0.7) + 3*(0.2) + 1*(0.1) = 2.13) 2*(0.7) + 1*(0.2) + 2*(0.1) = 1.84) 1*(0.7) + 3*(0.2) + 2*(0.1) = 1.55) 1*(0.7) + 2*(0.2) + 3*(0.1) = 1.4

Piąte drzewo jest optymalne.

Oczywiście znalezienie optymalnego drzewa wyszukiwania binarnego poprzez rozpatrzenie wszystkich drzew wiąże się z ilością drzew co najmniej wykładniczą w stosunku do n. W drzewie o głębokości n-1 wierzchołek na każdym z n-1 poziomów (oprócz korzenia) może się znajdować na prawo lub lewo. Zatem liczba różnych drzew o głębokości n-1 wynosi 2n-1

Załóżmy, że klucze od Keyi do Keyj są ułożone w drzewie, które minimalizuje wielkość: j

Σ cm pm m=i

gdzie cm jest liczbą porównań wymaganych do zlokalizowania klucza Keym w drzewie. Drzewo to nazywamy optymalnym.

Wartość optymalną oznaczymy jako A[i][j] oraz A[i][i]=pi

(jeden klucz wymaga jednego porównania).

Korzystając z przykładu można pokazać, że w problemie tym zachowana jest zasada optymalności.

Page 23: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Możemy sobie wyobrazić n różnych drzew optymalnych: drzewo 1 w którym Key1 jest w korzeniu, drzewo 2 w którym Key2 jest w korzeniu, … , drzewo n w którym Keyn jest w korzeniu. Dla 1 ≤ k ≤ n pod-drzewa drzewa k muszą być optymalne, więc czasy wyszukiwania w tych pod-drzewach można opisać:

Dla każdego m ≠ k wymagana jest o jeden większa liczba porównań w celu zlokalizowania klucza Keym w drzewie k niż w celu zlokalizowania tego klucza w poddrzewie w którym się znajduje. Dodatkowe porównanie jest związane z korzeniem i daje 1 * pm do średniego czasu wyszukiwania.

Średni czas wyszukiwania dla drzewa k wynosi

lub inaczej n

A[1][k-1] + A[k+1][n] + Σ pm

m=1

Page 24: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Jedno z k drzew musi być optymalne więc średni czas wyszukiwania optymalnego drzewa określa zależność: n

A[1][n] = minimum(A[1][k-1] + A[k+1][n]) + Σ pm

m=1

gdzie A[1][0] i A[n+1][n] są z definicji równe 0.

Uogólniamy definicje na klucze od Keyi do Keyj , gdzie i < j i otrzymujemy: j

A[i][j] = minimum(A[i][k-1] + A[k+1][j]) + Σ pm i < j i ≤ k ≤ j m=i

A[i][i] = pi

A[i][i-1] oraz A[j+1][j] są z definicji równe 0.

Wyliczenia prowadzimy podobnie jak w algorytmie łańcuchowego mnożenia macierzy.

Algorytm znajdowania optymalnego drzewa przeszukiwania binarnego. Problem: określenie optymalnego drzewa wyszukiwania binarnego dla zbioru kluczy, z których każdy posiada przypisane prawdopodobieństwo zostania kluczem wyszukiwania.

Dane: n-liczba kluczy oraz tablica liczb rzeczywistych p indeksowana od 1 do n, gdzie p[i] jest prawdopodobieństwem wyszukiwania i-tego klucza

Wyniki: zmienna minavg, której wartością jest średni czas wyszukiwania optymalnego drzewa wyszukiwania binarnego oraz tablica R, z której można skonstruło- wać drzewo optymalne.R[i][j] jest indeksem klucza

Page 25: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

znajdującego się w korzeniu drzewa optymalnego, zawierającego klucze od i-tego do j-tego.

void optsearch(int n, const float p[], float minavg, index R[][]){ index i, j, k, diagonal; float A[1..n+1][0..n]; for (i=1; i <= n; i++) { A[i][i-1] = 0; A[i][i] = p[i]; R[i][i] = i; R[i][i-1] = 0; } A[n+1][n] = 0; for(diagonal = 1; diagonal <= n-1; diagonal++) for(i = 1; i <= n - diagonal; i++) //Przekatna 1 { //tuz nad glowna przek j = i + diagonal; j

A[i][j]=minimum(A[i][k-1]+A[k+1][j] + Σ pm ; i ≤ k ≤ j m=i

R[i][j]= wartość k, która dała minimum; } minavg = A[1][n];}

Złożoność czasową można określić podobnie jak dla mnożenia łańcuchowego macierzy:

T(n) = n(n-1)(n+1)/6 ∈ Θ( n3 )

Page 26: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Algorytm budowania optymalnego drzewa przeszukiwania binarnego.

Problem: zbudować optymalne drzewo wyszukiwania binarnego.

Dane: n – liczba kluczy, tablica Key zawierająca n uporządkowa- nych kluczy oraz tablica R, utworzona w poprzednim algorytmie. R[i][j] jest indeksem klucza w korzeniu drzewa optymalnego, zawierającego klucze od i-tego do j-tego

Wynik: wskaźnik tree do optymalnego drzewa wyszukiwania binarnego, zawierającego n kluczy.

node_pointer tree(index i,j){ index k; node_pointer p;

k = R[i][j]; if(k == 0) return NULL; else { p = new nodetype; p->key = Key[k]; p->left = tree(i,k-1); p->right = tree(k+1,j); return p; }}

Przykład.Załóżmy, że mamy następujące wartości w tablicy Key: Damian Izabela Rudolf Waldemar Key[1] Key[2] Key[3] Key[4]

oraz p1 = 3/8 p2 = 3/8 p3 = 1/8 p4 = 1/8

Page 27: Podstawowe struktury danychstefanek/zfj/AiSD_Wyklad5_dzienne2012_struktury_danych.pdfPrima i algorytm Kruskala. Drzewa wyszukiwania binarnego ( i ich optymalizacja) Opracowujemy algorytm

Tablice A i R będą wówczas wyglądać: 0 1 2 3 4 0 1 2 3 4 1 | 0 3/8 9/8 11/8 7/4 1 |0 1 1 2 2 | | 2 | 0 3/8 5/8 1 2 | 0 2 2 2 | | 3 | 0 1/8 3/8 3 | 0 3 3 | | 4 | 0 1/8 4 | 0 4 | | 5 | 0 5 | 0

A R