Syllabus Wprowadzenie Poprawno

Podobne dokumenty
Wykład 5. Sortowanie w czasie liniowologarytmicznym

Algorytmy i złożoność obliczeniowa. Wojciech Horzelski

Wykład 4. Sortowanie

Wykład 2. Poprawność algorytmów

Wykład 3. Metoda dziel i zwyciężaj

Sortowanie w czasie liniowym

Zaawansowane algorytmy. Wojciech Horzelski

Wykład 6. Dynamiczne struktury danych

Sortowanie przez scalanie

Rekurencja. Dla rozwiązania danego problemu, algorytm wywołuje sam siebie przy rozwiązywaniu podobnych podproblemów. Przykład: silnia: n! = n(n-1)!

Struktura danych. Sposób uporządkowania informacji w komputerze. Na strukturach danych operują algorytmy. Przykładowe struktury danych:

Podstawy Informatyki. Sprawność algorytmów

Sortowanie - wybrane algorytmy

Sortowanie bąbelkowe

Podstawowe algorytmy i ich implementacje w C. Wykład 9

ZASADY PROGRAMOWANIA KOMPUTERÓW ZAP zima 2014/2015. Drzewa BST c.d., równoważenie drzew, kopce.

Algorytmy i Struktury Danych.

Zadanie 1 Przygotuj algorytm programu - sortowanie przez wstawianie.

Strategia "dziel i zwyciężaj"

Analiza algorytmów zadania podstawowe

TEORETYCZNE PODSTAWY INFORMATYKI

Struktura danych. Sposób uporządkowania informacji w komputerze. Na strukturach danych operują algorytmy. Przykładowe struktury danych:

prowadzący dr ADRIAN HORZYK /~horzyk tel.: Konsultacje paw. D-13/325

Algorytmy i Struktury Danych.

Zaawansowane algorytmy i struktury danych

Algorytmy i Struktury Danych

Matematyczne Podstawy Informatyki

Kolejka priorytetowa. Często rozważa się kolejki priorytetowe, w których poszukuje się elementu minimalnego zamiast maksymalnego.

Algorytm selekcji Hoare a. Łukasz Miemus

Zasady analizy algorytmów

Poprawność semantyczna

Wykład 2. Drzewa zbalansowane AVL i 2-3-4

Algorytmy i struktury danych Sortowanie IS/IO, WIMiIP

Za pierwszy niebanalny algorytm uważa się algorytm Euklidesa wyszukiwanie NWD dwóch liczb (400 a 300 rok przed narodzeniem Chrystusa).

Listy, kolejki, stosy

Laboratoria nr 1. Sortowanie

Algorytmy i struktury danych

Laboratorium nr 7 Sortowanie

Sortowanie. Kolejki priorytetowe i algorytm Heapsort Dynamiczny problem sortowania:

Sortowanie przez wstawianie

Porządek symetryczny: right(x)

Wykład 6. Drzewa poszukiwań binarnych (BST)

Analiza algorytmów zadania podstawowe

Struktury danych: stos, kolejka, lista, drzewo

Zadanie projektowe 1: Struktury danych i złożoność obliczeniowa

Definicja. Ciąg wejściowy: Funkcja uporządkowująca: Sortowanie polega na: a 1, a 2,, a n-1, a n. f(a 1 ) f(a 2 ) f(a n )

Programowanie w VB Proste algorytmy sortowania

Laboratoria nr 1. Sortowanie

Wykład 2. Drzewa poszukiwań binarnych (BST)

Złożoność algorytmów. Wstęp do Informatyki

Algorytmy i Struktury Danych, 2. ćwiczenia

Algorytmy i struktury danych. wykład 5

E S - uniwersum struktury stosu

Wstęp do programowania

Efektywna metoda sortowania sortowanie przez scalanie

Dynamiczny przydział pamięci w języku C. Dynamiczne struktury danych. dr inż. Jarosław Forenc. Metoda 1 (wektor N M-elementowy)

Sortowanie danych. Jolanta Bachan. Podstawy programowania

Teoretyczne podstawy informatyki

Algorytmy i Struktury Danych. (c) Marcin Sydow. Sortowanie Selection Sort Insertion Sort Merge Sort. Sortowanie 1. Listy dowiązaniowe.

Drzewa poszukiwań binarnych

Teoretyczne podstawy informatyki

Sortowanie. Bartman Jacek Algorytmy i struktury

Wstęp do programowania

Wysokość drzewa Głębokość węzła

Algorytmy sortujące i wyszukujące

Algorytmy i. Wykład 5: Drzewa. Dr inż. Paweł Kasprowski

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Algorytmy i struktury danych Laboratorium 7. 2 Drzewa poszukiwań binarnych

WYŻSZA SZKOŁA INFORMATYKI STOSOWANEJ I ZARZĄDZANIA

Struktury danych i złożoność obliczeniowa Wykład 2. Prof. dr hab. inż. Jan Magott

Wstęp do programowania

Wyszukiwanie w BST Minimalny i maksymalny klucz. Wyszukiwanie w BST Minimalny klucz. Wyszukiwanie w BST - minimalny klucz Wersja rekurencyjna

Drzewa poszukiwań binarnych

Wprowadzenie do złożoności obliczeniowej

dr inż. Paweł Myszkowski Wykład nr 11 ( )

Programowanie proceduralne INP001210WL rok akademicki 2017/18 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Algorytmy i struktury danych. Drzewa: BST, kopce. Letnie Warsztaty Matematyczno-Informatyczne

3. Podaj elementy składowe jakie powinna uwzględniać definicja informatyki.

Literatura. 1) Pojęcia: złożoność czasowa, rząd funkcji. Aby wyznaczyć pesymistyczną złożoność czasową algorytmu należy:

Wykład 3. Złożoność i realizowalność algorytmów Elementarne struktury danych: stosy, kolejki, listy

. Podstawy Programowania 2. Drzewa bst - część druga. Arkadiusz Chrobot. 12 maja 2019

dodatkowe operacje dla kopca binarnego: typu min oraz typu max:

Teoretyczne podstawy informatyki

EGZAMIN - Wersja A. ALGORYTMY I STRUKTURY DANYCH Lisek89 opracowanie kartki od Pani dr E. Koszelew

Algorytmy i struktury danych. Co dziś? Tytułem przypomnienia metoda dziel i zwyciężaj. Wykład VIII Elementarne techniki algorytmiczne

ALGORYTMY I STRUKTURY DANYCH

Algorytmy i Struktury Danych. Co dziś? Drzewo decyzyjne. Wykład IV Sortowania cd. Elementarne struktury danych

Algorytmy sortujące 1

Temat: Liniowe uporzdkowane struktury danych: stos, kolejka. Specyfikacja, przykładowe implementacje i zastosowania. Struktura słownika.

Podstawy Informatyki. Metody dostępu do danych

Drzewa BST i AVL. Drzewa poszukiwań binarnych (BST)

Przy pomocy indukcji udowodnimy, że nastąpi koniec świata, a warto byłoby wiedzieć kiedy, czy przed czy po egzaminie.

Drzewo. Drzewo uporządkowane ma ponumerowanych (oznaczonych) następników. Drzewo uporządkowane składa się z węzłów, które zawierają następujące pola:

Programowanie Proceduralne

Teoretyczne podstawy informatyki

Algorytm. Krótka historia algorytmów

Algorytmy i Struktury Danych, 9. ćwiczenia

Wykład 3. Drzewa czerwono-czarne

Sortowanie. LABORKA Piotr Ciskowski

Podstawy programowania 2. Temat: Drzewa binarne. Przygotował: mgr inż. Tomasz Michno

Rekurencje. Jeśli algorytm zawiera wywołanie samego siebie, jego czas działania moŝe być określony rekurencją. Przykład: sortowanie przez scalanie:

Transkrypt:

Syllabus Wprowadzenie Poprawność algorytmów (analiza algorytmów) Sortowanie Elementarne struktury danych Wyszukiwanie Zaawansowane struktury danych Programowanie dynamiczne 1

Literatura T. Cormen, Ch. Lieserson, R. Rivest, Wprowadzenie do Algorytmów, WNT, 1997 R. Sedgewick, Algorytmy w C++, RM, 1999 N. Wirth, Algorytmy + struktury danych = programy, WNT, 2001 2

O co w tym wszystkim chodzi? Rozwiązywanie problemów: Układanie planu zajęć Balansowanie własnego budżet Symulacja lotu samolotem Prognoza pogody Dla rozwiązania problemów potrzebujemy procedur, recept, przepisów inaczej mówiąc algorytmów 3

Historia Nazwa pochodzi od perskiego matematyka Muhammeda ibn Musa Alchwarizmiego (w łacińskiej wersji Algorismus) IX w n.e. Pierwszy dobrze opisany algorytm algorytm Euklidesa znajdowania największego wspólnego podzielnika, 400-300 p.n.e. XIX w. Charles Babbage, Ada Lovelace. XX w. Alan Turing, Alonzo Church, John von Neumann 4

Struktury danych i algorytmy Algorytm metoda, zestaw działań (instrukcji) potrzebnych do rozwiązania problemu Program implementacja algorytmu w jakimś języku programowania Struktura danych organizacja danych niezbędna dla rozwiązania problemu (metody dostępu etc.) 5

Ogólne spojrzenie Wykorzystanie komputera: Projektowanie programów (algorytmy, struktury danych) Pisanie programów (kodowanie) Weryfikacja programów (testowanie) Cele algorytmiczne: - poprawność, - efektywność, Cele implementacji: - zwięzłość - możliwość powtórnego wykorzystania 6

Problemy algorytmiczne Specyfikacja wejścia? Specyfikacja wyjścia, jako funkcji wejścia Ilość instancji danych spełniających specyfikację wejścia może być nieskończona, np.: posortowana niemalejąco sekwencja liczb naturalnych, o skończonej długości: 1, 20, 908, 909, 100000, 1000000000. 3, 44, 211, 222, 433. 3. 7

Rozwiązanie problemu Instancja wejściowa (dane), odpowiadająca specyfikacji algorytm Wyniki odpowiadające danym wejściowym Algorytm opisuje działania, które mają zostać przeprowadzone na danych Może istnieć wiele algorytmów rozwiązujących ten sam problem 8

Definicja algorytmu Algorytmem nazywamy skończoną sekwencję jednoznacznych instrukcji pozwalających na rozwiązanie problemu, tj. na uzyskanie pożądanego wyjścia dla każdego legalnego wejścia. Własności algorytmów: określoność skończoność poprawność ogólność dokładność 9

Przykład 1: poszukiwanie Wejście: uporządkowany niemalejąco ciąg n (n >0) liczb liczba a 1, a 2, a 3,.,a n ; q Wyjście: indeks (pozycja) odnalezionej wartości lub NIL j 2 5 4 10 11; 5 2 2 5 4 10 11; 9 NIL 10

Przykład 1: poszukiwanie liniowe INPUT: INPUT: A[1..n] A[1..n] tablica tablica liczb, liczb, q liczba liczba całkowita. OUTPUT: indeks indeksj j taki, taki, że żea[j] = q. q. NIL, NIL, jeśli jeśli j j (1 j n): A[j] A[j] q j 1 j 1 while while j n nand A[j] A[j] q q do doj++ if if j n nthen thenreturn j else else return NIL NIL Algorytm wykorzystuje metodę siłową (brute-force) przegląda kolejno elementy tablicy. Kod napisany jest w jednoznacznym pseudojęzyku (pseudokodzie). Wejście (INPUT) i wyjście (OUTPUT) zostały jasno określone. 11

Pseudokod Zbliżony do Ady, C, Javy czy innego języka programowania: struktury sterujące (if then else, pętle while i for) przypisanie ( ) dostęp do elementów tablicy: A[i] dla typów złożonych (record lub object) dostęp do pól: A.b zmienna reprezentująca tablicę czy obiekt jest traktowana jak wskaźnik do tej struktury (podobnie, jak w C). 12

Warunki początkowe i końcowe (precondition, postcondition) Ważne jest sprecyzowanie warunków początkowego i końcowego dla algorytmu: INPUT: określenie jakie dane algorytm powinien dostać na wejściu OUTPUT: określenie co algorytm powinien wyprodukować. Powinna zostać przewidziana obsługa specjalnych przypadków danych wejściowych 13

Przykład 2: sortowanie Wejście ciąg n liczb Wyjście Permutacja wejściowego ciągu a 1, a 2, a 3,.,a n Sort b 1,b 2,b 3,.,b n 2 5 4 10 7 2 4 5 7 10 poprawność wyjścia: Dla Dla każdego wejścia algorytm po po zakończeniu działania powinien dać daćjako wynik b 1, 1, b 2, 2,, b n takie, n że: że: b 1 < 1 b 2 < 2 b 3 < 3.. < b n n b 1, 1, b 2, 2, b 3, 3,., b n jest n jestpermutacją a 1, 1, a 2, 2, a 3,.,a 3 n n 14

Sortowanie przez wstawianie (Insertion Sort) A Strategia zaczynamy od od pustej pustej ręki ręki wkładamy kartę kartęwe we właściwe miejsce kart kart poprzednio już już posortowane kontynuujemy takie takie postępowanie aż ażwszystkie karty karty zostaną wstawione 3 4 6 8 9 7 2 5 1 1 j n i INPUT: INPUT: A[1..n] A[1..n] tablica tablica liczb liczb całkowitych OUTPUT: permutacja A taka, taka, że że A[1] A[1] A[2] A[n] for forj 2 to to n do dokey A[j] wstaw wstawa[j] A[j] do do posortowanej sekwencji A[1..j-1] i j-1 while while i>0 i>0 and and A[i]>key do do A[i+1] A[i] i-- i-- A[i+1] key 15

Analiza algorytmów Efektywność: Czas działania Wykorzystanie pamięci Efektywność jako funkcja rozmiaru wejścia: Ilość danych wejściowych (liczb, punktów, itp.) Ilość bitów w danych wejściowych 16

Analiza sortowania przez wstawianie Określany czas wykonania jako funkcję rozmiaru wejścia for j 2 to n do key A[j] wstaw A[j] do posortowanej sekwencji A[1..j-1] i j-1 while i>0 and A[i]>key do A[i+1] A[i] i-- A[i+1]:=key czas c 1 c 2 0 c 3 c 4 c 5 c 6 c 7 ile razy n n-1 n-1 n-1 n t j 2 j n ( t 1) j 2 j n = ( t 1) j 2 j = n-1 = 17

Przypadki: najlepszy/najgorszy/średni Najlepszy przypadek: elementy już są posortowane t j =1, czas wykonania liniowy (Cn). Najgorszy przypadek: elementy posortowane nierosnąco (odwrotnie posortowane) t j =j, czas wykonania kwadratowy (Cn 2 ) Przypadek średni : t j =j/2, czas wykonania kwadratowy (Cn 2 ) 18

Przypadki: najlepszy/najgorszy/średni Dla ustalonego n czas wykonania dla poszczególnych instancji: 6n 5n 4n 3n 2n 1n 19

Przypadki: najlepszy/najgorszy/średni Dla różnych n: najgorszy przypadek Czas działania 6n 5n 4n 3n 2n 1n średni przypadek najlepszy przypadek 1 2 3 4 5 6 7 8 9 10 11 12.. Rozmiar wejścia 20

Przypadki: najlepszy/najgorszy/średni Analizę najgorszego przypadku stosuje się zwykle wtedy, kiedy czas działania jest czynnikiem krytycznym (kontrola lotów, sterowanie podawaniem leków itp.) Dla pewnych zadań najgorsze przypadki mogą występować dość często. Określenie przypadku średniego (analiza probabilistyczna) jest często bardzo kłopotliwe 21

Różnice w podejściu? Czy sortowanie przez wstawianie jest najlepszą strategią dla zadania sortowania? Rozważmy alternatywną strategię opartą o zasadę dziel i zwyciężaj : Sortowanie przez łączenie (MergeSort): ciąg <4, 1, 3, 9> dzielimy na dwa podciągi Sortujemy te podciągi: <4, 1> i <3, 9> łączymy wyniki Czas wykonania rzędu n log n 22

Analiza wyszukiwania INPUT: INPUT: A[1..n] A[1..n] tablica tablica liczb liczb całkowitych, q liczba liczba całkowita OUTPUT: indeks indeksj j taki, taki, że że A[j] A[j] = q. q. NIL, NIL, jeśli jeśli j j (1 j n): A[j] A[j] q j 1 j 1 while while j n nand A[j] A[j] q q do doj++ if if j n nthen thenreturn j else else return NIL NIL Najgorszy przypadek: C n Średni przypadek: C n/2 23

Poszukiwanie binarne Pomysł: dziel i zwyciężaj INPUT: INPUT: A[1..n] A[1..n] posortowana tablica tablica liczb liczb całkowitych, q liczba liczba całkowita. OUTPUT: indeks indeksj j taki, taki, że żea[j] = q. q. NIL, NIL, jeśli jeśli j j (1 j n): A[j] A[j] q left 1 right n do do j (left+right)/2 if if A[j]=q then thenreturn j else else if if A[j]>q then then right j-1 else else left=j+1 while while left<=right return NIL NIL 24

Poszukiwanie binarne - analiza Ile razy wykonywana jest pętla: Po każdym przebiegu różnica międzyleft aright zmniejsza się o połowę początkowo n pętla kończy się kiedy różnica wynosi 1 lub 0 Ile razy trzeba połowić n żeby dostać 1? lg n lepiej niż poprzedni algorytm (n) 25

Poprawność algorytmów 26

Przegląd Poprawność algorytmów Podstawy matematyczne: Przyrost funkcji i notacje asymptotyczne Sumowanie szeregów Indukcja matematyczna 27

Poprawność algorytmów Algorytm jest poprawny jeżeli dla każdego legalnego wejścia kończy swoje działanie i tworzy pożądany wynik. Automatyczne dowiedzenie poprawności nie możliwe Istnieją jednak techniki i formalizmy pozwalające na dowodzenie poprawności algorytmów 28

Poprawność praktyczna i całkowita Praktyczna Jeśli ten punkt został osiągnięty to otrzymaliśmy poprawny wynik Poprawne dane algorytm Wynik Całkowita poprawność Zawsze ten punkt zostanie osiągnięty i otrzymamy poprawny wynik Poprawne dane algorytm Wynik 29

Dowodzenie W celu dowiedzenia poprawności algorytmu wiążemy ze specyficznymi miejscami algorytmu stwierdzenia (dotyczące stanu wykonania). np., A[1],, A[k] są posortowane niemalejąco Warunki początkowe (Precondition) stwierdzenia, których prawdziwość zakładamy przed wykonaniem algorytmu lub podprogramu (INPUT) Warunki końcowe (Postcondition) stwierdzenia, które muszą być prawdziwe po wykonaniu algorytmu lub podprogramu (OUTPUT) 30

Niezmienniki pętli Niezmienniki stwierdzenia prawdziwe za każdym razem kiedy osiągany jest pewien punkt algorytmu (może to zdarzać się wielokrotnie w czasie wykonania algorytmu, np. w pętli) Dla niezmienników pętli należy pokazać : Inicjalizację prawdziwość przed pierwszą iteracją Zachowanie jeśli stwierdzenie jest prawdziwe przed iteracją to pozostaje prawdziwe przed następną iteracją Zakończenie kiedy pętla kończy działanie niezmiennik daje własność przydatną do wykazania poprawności algorytmu 31

Przykład: poszukiwanie binarne (1) Chcemy mieć pewność, że jeżeli zwracany jest NIL to wartości q nie ma w tablicy A niezmiennik: na początku każdego wykonania pętli while A[i] < q dla każdego i [1..left-1] oraz A[i] > q dla każdego i [right+1..n] left 1 right n do do j (left+right)/2 if if A[j]=q then thenreturn j else else if if A[j]>q then then right j-1 else else left=j+1 while while left<=right return NIL NIL inicjalizacja: left = 1, right = n niezmiennik jest prawdziwy (nie ma elementów w [1..left-1] i [right+1..n] ) 32

Przykład: poszukiwanie binarne (2) niezmiennik: na początku każdego wykonania pętli while A[i] < q dla każdego i [1..left-1] oraz A[i] > q dla każdego i [right+1..n] left 1 right n do do j (left+right)/2 if if A[j]=q then thenreturn j else else if if A[j]>q then then right j-1 else else left=j+1 while while left<=right return NIL NIL zachowanie: jeśli A[j]>q, to A[i] > q dla wszystkich i [j..n], ponieważ tablica jest posortowana. Wtedy przypisano j-1 do right. Stąd, druga część niezmiennika również zachodzi. Analogicznie pokazuje się pierwszą część. 33

Przykład: poszukiwanie binarne (3) niezmiennik: na początku każdego wykonania pętli while A[i] < q dla każdego i [1..left-1] oraz A[i] > q dla każdego i [right+1..n] left 1 right n do do j (left+right)/2 if if A[j]=q then thenreturn j else else if if A[j]>q then then right j-1 else else left=j+1 while while left<=right return NIL NIL Zakończenie: kiedy pętla kończy działanie, mamy left > right. Niezmiennik oznacza, że q jest mniejsze od wszystkich elementów A na lewo od left oraz większy od wszystkich elementów A na prawo od right. To wyczerpuje wszystkie elementy A. 34

Przykład: sortowanie przez wstawianie niezmiennik: na początku każdego wykonania pętli for, A[1 j-1] składa się z posortowanych elementów for forj=2 to to length(a) do dokey key A[j] i j-1 j-1 while while i>0 i>0 and and A[i]>key do do A[i+1] A[i] A[i] i-- i-- A[i+1] key key inicjalizacja: j = 2, niezmiennik jest trywialny, A[1] jest zawsze posortowana zachowanie: wewnątrz pętli while przestawia się elementy A[j-1], A[j-2],, A[j-k] o jedną pozycję bez zmiany ich kolejności. Element A[j] jest wstawiany na k-tą pozycję, tak że A[k-1] A[k] A[k+1]. Stąd A[1..j-1] jest posortowane. zakończenie: kiedy pętla się kończy (j=n+1) niezmiennik oznacza, że cała tablica została posortowana. 35

Notacje asymptotyczne Cel: uproszczenie analizy czasy wykonania, zaniedbywanie szczegółów, które mogą wynikać ze specyficznej implementacji czy sprzętu zaokrąglanie dla liczb: 1,000,001 1,000,000 zaokrąglanie dla funkcji: 3n 2 n 2 Główna idea: jak zwiększa się czas wykonania algorytmu wraz ze wzrostem rozmiaru wejścia (w granicy). Algorytm asymptotycznie lepszy będzie bardziej efektywny dla prawie wszystkich rozmiarów wejść (z wyjątkiem być może małych ) 36

Notacje asymptotyczne Notacja O (duże O) Asymptotyczne ograniczenie górne f(n) = O(g(n)), jeżeli istnieje stała c i n 0, takie, że f(n) c g(n) dla n n 0 f(n) i g(n) są nieujemnymi funkcjami całkowitymi Korzysta się z niej przy analizie najgorszego przypadku. Czas działania c g( n) f (n) n0 Rozmiar wejścia 37

Notacje asymptotyczne Notacja Ω (duża Ω) Asymptotyczne ograniczenie dolne f(n) = Ω(g(n)) jeśli istnieje stała c i n 0, takie, że c g(n) f(n) dla n n 0 Opisuje najlepsze możliwe zachowanie się algorytmu Czas działania f (n ) c g( n) n 0 Rozmiar wejścia 38

Notacje asymptotyczne Prosta zasada: odrzucamy mniej istotne dla czasu składniki i czynniki stałe. 50 n log n jest O(n log n) 7n - 3 jest O(n) 8n 2 log n + 5n 2 + n jest O(n 2 log n) O jest ograniczeniem górnym więc np. (50 n log n) jest typu O(n 5 ), ale interesuje nas najlepsze możliwe oszacowanie w tym przypadku jest to O(n log n) 39

Notacje asymptotyczne Notacja Θ (duża Θ ) Dokładne oszacowanie asymptotyczne f(n) = Θ(g(n)) jeżeli istnieją stałe c 1, c 2, i n 0, takie, że c 1 g(n) f(n) c 2 g(n) dla n n 0 f(n) = Θ(g(n)) wtedy i tylko wtedy, gdy f(n) = Ο(g(n)) i f(n) = Ω(g(n)) Czas działania c g (n) 2 f (n ) c g (n) 1 n 0 Rozmiar wejścia 40

Notacje asymptotyczne Istnieją dwie inne notacje asymptotyczne: małe o" f(n)=o(g(n)) mocniejsze ograniczenie analogiczne do O Dla każdego c, musi istnieć n 0, takie, że f(n) c g(n) dla n n 0 mała omega" f(n)=ω(g(n)) analogicznie dla Ω 41

Notacje asymptotyczne Analogie do zależności pomiędzy liczbami: f(n) = O(g(n)) f g f(n) = Ω(g(n)) f g f(n) = Θ(g(n)) f = g f(n) = o(g(n)) f < g f(n) = ω(g(n)) f > g Zwykle zapisujemy: f(n) = O(g(n)), co formalnie powinno być rozumiane jako f(n) O(g(n)) 42

Porównanie czasów wykonania Maksymalny rozmiar problemu (n) 1 sekunda 1 minuta 1 godzina 400n 2500 150000 9000000 20n log n 4096 166666 7826087 2n 2 707 5477 42426 n 4 31 88 244 2 n 19 25 31 43

Szeregi Szereg geometryczny Dana jest liczba całkowita n 0 i rzeczywiste 0< a 1 n i= 0 1 a = + + + + = 1 a i 2 n a 1 a a... a n+ 1 Szereg geometryczny reprezentuje przyrost wykładniczy Szereg arytmetyczny n i= 0 i = 1+ 2 + 3 +... + n = n(1 + n) 2 Przyrost kwadratowy 44

Sumowanie Czas działania sortowania przez wstawianie jest zdeterminowany przez zagnieżdżone pętlę for j 2 to n do key A[j] wstaw A[j] do posortowanej sekwencji A[1..j-1] i j-1 while i>0 and A[i]>key do A[i+1] A[i] i-- A[i+1]:=key Czas wykonania pętli reprezentuje szereg n ( 1) ( 2 j = O n ) j= 2 czas c 1 c 2 0 c 3 c 4 c 5 c 6 c 7 ile razy n n-1 n-1 n-1 n t j 2 j n ( t 1) j 2 j n = ( t 1) j 2 j = n-1 = 45

Dowody indukcyjne Chcemy pokazać prawdziwość własności P dla wszystkich liczb całkowitych n n 0 Założenie indukcyjne: dowodzimy prawdziwości P dla n 0 Krok indukcyjny: dowodzimy, że z prawdziwości P dla wszystkich k, n 0 k n 1 wynika prawdziwość P dla n Przykład: n S( n ) = i= 0 i = n( n + 1), 2 dla n 1 Założenie ind. S(1) 1 = i = i= 0 1(1 + 1) 2 46

Dowody indukcyjne Krok indukcyjny k k( k + 1) S( k) = i = for dla 1 k n 1 2 i= 0 n n 1 S( n) = i = i + n = S( n 1) + n = i= 0 i= 0 2 ( n 1+ 1) ( n n + 2 n) = ( n 1) 2 + n = 2 = n( n + 1) = 2 47

Metoda dziel i zwyciężaj 48

Wprowadzenie Technika konstrukcji algorytmów dziel i zwyciężaj. przykładowe problemy: Wypełnianie planszy Poszukiwanie (binarne) Sortowanie (sortowanie przez łączenie - merge sort). 49

Wypełnianie planszy Zadanie: dysponując klockami oraz planszą 2 n x2 n z brakującym polem: Wypełnić plansze w całości: 50

Wypełnianie planszy: przypadki trywialne (n = 1) Przypadek trywialny (n = 1): wypełniamy plansze jednym klockiem: Idea dla rozwiązania problemu doprowadzić rozmiar zadania do przypadku trywialnego, który umiemy rozwiązać 51

Wypełnianie planszy : podział zadania Oryginalną planszę dzielimy na 4 części Dostajemy problemy o rozmiarze 2 n-1 x2 n-1 Ale: trzy z nich nie są podobne do oryginalnego (plansze nie mają brakującego pola)! 52

Wypełnianie planszy : podział zadania pomysł: umieszczamy jeden klocek w środku planszy i dokonujemy podziału na 4 części Teraz otrzymujemy 4 plansze o rozmiarach 2 n-1 x2 n-1. Każda z planszy ma brakujące pole 53

Wypełnianie planszy : algorytm INPUT: INPUT: n plansza 2 n n x2 x2 n n,, L pozycja brakującego pola. pola. OUTPUT: wypełniona plansza Tile(n, L) L) if if n = 1 then then przypadek trywialny wypełnij jednym klockiem return umieść jeden jeden klocek w środku planszy podziel planszę na na 4 równe równe części Niech NiechL1, L2, L2, L3, L3, L4 L4 oznaczają pozycje 4 brakujących pól pól Tile(n-1, L1) L1) Tile(n-1, L2) L2) Tile(n-1, L3) L3) Tile(n-1, L4) L4) 54

Dziel i zwyciężaj Metoda konstrukcji algorytmów Dziel i zwyciężaj : Jeśli problem jest na tyle mały, że umiesz go rozwiązać - zrób to. Jeśli nie to: Podział: Podziel problem na dwa lub więcej rozdzielnych podproblemów Rozwiązanie: Wykorzystaj metodę rekurencyjnie dla rozwiązania tych podproblemów Łączenie: połącz rozwiązania podproblemów tak, aby rozwiązać oryginalny problem 55

Wypełnianie planszy : Dziel i zwyciężaj Wypełnianie jest przykładem algorytmu dziel i zwyciężaj : w wypadku trywialnym (2x2) po prostu wypełniamy planszę, lub: Dzielimy planszę na 4 mniejsze części (wprowadzając wypełnione miejsce w rogu, przez umieszczenie centralnie jednego klocka) Rozwiązujemy problem rekursywnie stosując tą samą metodę Łączymy części umieszczając klocek w środku planszy 56

Poszukiwanie binarne Odnaleźć liczbę w posortowanej tablicy: Przypadek trywialny tablica jest jednoelementowa Albo dzielimy tablice na dwie równe części i rozwiązujemy zadanie osobno dla każdej z nich INPUT: INPUT: A[1..n] A[1..n] posortowana niemalejąco tablica tablica liczb, liczb, s liczba. liczba. OUTPUT: indeks indeksj j taki, taki, że żea[j] = s. s. NIL, NIL, jeśli jeśli j j (1 j n): A[j] A[j] s Binary-search(A, p, p, r, r, s): s): if if p = r then then if if A[p] A[p] = s then then return p else else return NIL NIL q (p+r)/2 ret ret Binary-search(A, p, p, q, q, s) s) if if ret ret = NIL NILthen return Binary-search(A, q+1, q+1, r, r, s) s) else else return ret ret 57

Rekurencja Czas działania algorytmu z odwołaniami rekursywnymi można opisać poprzez rekurencję Równanie/nierówność opisująca funkcję poprzez jej wartości dla mniejszego argumentu Przykład: poszukiwanie binarne T ( n) Θ (1) if n = 1 = 2 T ( n / 2) + Θ (1) if n > 1 Po rozwiązaniu daje to złożoność O(n)! taką samą jak dla metody naiwnej 58

Poszukiwanie binarne (poprawione) T(n) = Θ(n) nie lepiej niż dla metody siłowej! Poprawa: rozwiązywać zadanie tylko dla jednej połowy tablicy INPUT: INPUT: A[1..n] A[1..n] posortowana niemalejąco tablica tablica liczb, liczb, s liczba. liczba. OUTPUT: indeks indeksj j taki, taki, że żea[j] = s. s. NIL, NIL, jeśli jeśli j j (1 j n): A[j] A[j] s s Binary-search(A, p, p, r, r, s): s): if if p = r then then if if A[p] A[p] = s then then return p else else return NIL NIL q (p+r)/2 if if A[q] A[q] s then then return Binary-search(A, p, p, q, q, s) s) else else return Binary-search(A, q+1, q+1, r, r, s) s) 59

Czas działania metody T ( n) Θ (1) if n = 1 = T ( n / 2) + Θ (1) if n > 1 T(n) = Θ(lg n)! 60

Sortowanie przez łączenie (merge sort) Podziel: Jeśli S posiada przynajmniej dwa elementy (1 lub 0 elementów przypadek trywialny), podziel S na dwie równe (z dokładnością do 1 elementu) części S 1 i S 2. (tj. S 1 zawiera pierwsze n/2 elementów, a S 2 kolejne n/2 ). Zwyciężaj: posortuj sekwencje S 1 i S 2 stosując Merge Sort. Połącz: Połącz elementy z dwóch posortowanych sekwencji S 1 i S 2 w sekwencję S zachowaniem porządku 61

Algorytm Merge Sort Merge-Sort(A, p, p, r) r) if if p < r then then q (p+r)/2 Merge-Sort(A, p, p, q) q) Merge-Sort(A, q+1, q+1, r) r) Merge(A, p, p, q, q, r) r) Merge(A, p, p, q, q, r) r) wybieramy mniejszy z dwóch dwóch elementów na na początku sekwencji A[p..q] oraz oraza[q+1..r] i wkładamy go go do do sekwencji wynikowej, przestawiamy odpowiedni znacznik. Powtarzamy to to aż ażdo do wyczerpania się sięelementów. Rezultat kopiujemy do do A[p..r]. 62

Sortowanie przez łączenie - 1 63

Sortowanie przez łączenie - 2 64

Sortowanie przez łączenie - 3 65

Sortowanie przez łączenie - 4 66

Sortowanie przez łączenie - 5 67

Sortowanie przez łączenie - 6 68

Sortowanie przez łączenie - 7 69

Sortowanie przez łączenie - 8 70

Sortowanie przez łączenie - 9 71

Sortowanie przez łączenie - 10 72

Sortowanie przez łączenie - 11 73

Sortowanie przez łączenie - 12 74

Sortowanie przez łączenie - 13 75

Sortowanie przez łączenie - 14 76

Sortowanie przez łączenie - 15 77

Sortowanie przez łączenie - 16 78

Sortowanie przez łączenie - 17 79

Sortowanie przez łączenie - 18 80

Sortowanie przez łączenie - 19 81

Sortowanie przez łączenie - 20 82

Sortowanie przez łączenie - 21 83

Sortowanie przez łączenie - 22 84

Sortowanie przez łączenie podsumowanie Sortowanie n liczb jeśli n=1 trywialne rekursywnie sortujemy 2 ciągi n/2 i n/2 liczb łączymy dwa ciągi w czasie Θ(n) Strategia Podział problemu na mniejsze, ale analogiczne podproblemy Rekursywne rozwiązywanie podproblemów Łączenie otrzymanych rozwiązań 85

Sortowanie przez łączenie czas działania Czas działania algorytmu może być reprezentowany przez następującą zależność rekurencyjną: T ( n) Θ (1) if n = 1 = 2 T ( n / 2) + Θ ( n) if n > 1 Po rozwiązaniu dostajemy: T ( n) = Θ( nlg n) 86

Wieże Hanoi Mamy 3 wieże oraz stos 64 dysków o zmniejszających sięśrednicach umieszczonych na pierwszej wieży Potrzebujemy przenieść wszystkie dyski na inną wieżę Zabronione jest położenie dysku większego na mniejszym W każdym kroku wolno mam przenieść tylko jeden dysk 87

Wieże Hanoi 88

Rozwiązanie rekursywne 89

Algorytm rekursywny INPUT: INPUT: n ilość ilośćdysków,, a, a, b, b, c wieże, wieże, wieża wieża a zawiera wszystkie dyski. dyski. OUTPUT: a, a, b, b, c wieże, wieże, wieża wieża b zawiera wszystkie dyski dyski Hanoi(n, a, a, b, b, c) c) if if n = 1 then then Move(a,b); else else Hanoi(n-1,a,c,b); Move(a,b); Hanoi(n-1,c,b,a); Poprawność algorytmu łatwo pokazać przez indukcję względem n. 90

Ilość kroków n M(n) Ilość kroków M(n) potrzebnych do rozwiązania problemu dla n dysków spełnia zależność rekurencyjną M(1) = 1 M(n) = 2M(n-1) + 1 1 2 3 1 3 7 4 15 5 31 91

Ilość kroków Rozwijając tę zależność dostajemy M(n) = 2M(n-1) + 1 = 2*[2*M(n-2)+1] + 1 = 2 2 * M(n-2) + 1+2 = 2 2 * [2*M(n-3)+1] + 1 + 2 = 2 3 * M(n-3) + 1+2 + 2 2 = Po k krokach M(n) = 2 k * M(n-k) + 1+2 + 2 2 + + 2 n-k-1 Dla k = n-1 M(n) = 2 n-1 * M(1) + 1+2 + 2 2 + + 2 n-2 = 1 + 2 + + 2 n-1 = 2 n -1 92

Sortowanie 93

Sortowanie - zadanie Definicja (dla liczb): wejście: ciąg n liczb A = (a 1, a 2,, a n ) wyjście: permutacja (a 1,, a n ) taka, że a 1 a n Po co sortować? Podstawowy problem dla algorytmiki Wiele algorytmów wykorzystuje sortowanie jako procedurę pomocniczą Pozwala pokazać wiele technik Dobrze zbadane (czas) 94

Sortowanie taksonomia Wewnętrzne i zewnętrzne Zależnie od miejsca przechowywania zbioru: (RAM czy dysk) Sortowanie tablic i sortowanie list łączonych zależnie od struktury danych (pliku); różny sposób dostępu (bezpośredni dla tablicy, sekwencyjny dla listy). W miejscu lub nie Nie wymaga dodatkowej pamięci Stabilne i niestabilne Kolejność elementów o tych samych wartościach klucza nie zmienia się. Inaczej kolejne sortowanie dla złożonych obiektów nie psuje efektów poprzedniego sortowania. Bezpośrednie i pośrednie zależnie od tego przemieszczamy całe obiekty, czy tylko wskaźniki (indeksy) do nich 95

Zestawienie czasów działania Przez wybór: O(N 2 ) zawsze Bąbelkowe: O(N 2 ) najgorszy przypadek; O(N) najlepszy przyp. Wstawianie: O(N 2 ) średnio; O(N) najlepszy przypadek Shellsort: O(N 4/3 ) Heapsort: O(NlogN) zawsze Mergesort: O(NlogN) zawsze Quicksort: O(NlogN) średnio; O(N 2 ) najgorszy przypadek Zliczanie: O(N) zawsze Radix sort: O(N) zawsze zewnętrzne: O(b logb)) dla pliku o b stronach. 96

Sortowanie przez wybór pomysł Znajdujemy najmniejszy element ciągu i zamieniamy go z pierwszym elementem. Powtarzamy to dla podciągu bez pierwszego elementu, itd. X X Znajdź minimum i zamień z pierwszym elementem 97

Sortowanie przez wybór pseudokod Selection_Sort(int A) 1 for i 1 to length[a] 2 do min i; 3 for j i+1 to length[a] 4 do if A[j] < A[min] then min j; 5 Exchange A[min] A[i] 98

Sortowanie przez wybór przykład iteracja 1 2 3 4 5 6 7 8 9 10 11 ciąg: EASYQUESTION - rozmiar 12 znaków #porównań #zamian EASYQUESTION AESYQUESTION 11 1 AESYQUESTION 10 1 AEEYQUSSTION 9 1 AEEIQUSSTYON 8 1 AEEINUSSTYOQ 7 1 AEEINOSSTYUQ 6 1 AEEINOQSTYUS 5 1 AEEINOQSTYUS 4 1 AEEINOQSSYUT 3 1 AEEINOQSSTUY 2 1 AEEINOQSSTUY 1 1 Razem 66 11 99

Sortowanie przez wybór czas działania Zależność od danych wejściowych: Ilość przebiegów: nie (zawsze N-1) Ilość porównań: nie Ilość zamian: nie O(N 2 ) zawsze (bez znaczenia jaki jest układ elementów w danych ważna tylko ilość) 100

Sortowanie bąbelkowe (przez zamianę) Przechodzimy przez ciąg od jego końca, porównując sąsiadujące elementy i ewentualnie zamieniając je miejscami. Powtarzamy te procedurę aż do uporządkowania całego ciągu. Po pierwszym przejściu element minimalny znajduje się na początku a[0], po drugim na drugim miejscu znajduje się drugi co do wielkości a[1], po itd. Porównanie do wypływających bąbelków stąd nazwa. 101

Sortowanie bąbelkowe pseudokod BUBBLE_SORT(A) 1 for i 1 to length[a] 2 do for j length[a] downto i + 1 3 do if A[j] < A[j - 1] 4 then exchange A[j] A[j - 1] 102

Sortowanie bąbelkowe przykład iteracja 1 2 3 4 5 6 7 8 9 Ciąg: EASYQUESTION, (12 znaków) porównań zamian EASYQUESTION (najgorszy przyp) AEESYQUISTNO 11 (11) 8 (11) AEEISYQUNSTO 10 (10) 6 (10) AEEINSYQUOST 9 (9) 6 (9) AEEINOSYQUST 8 (8) 4 (8) AEEINOQSYSUT 7 (7) 3 (7) AEEINOQSSYTU 6 (6) 2 (6) AEEINOQSSTYU 5 (5) 1 (5) AEEINOQSSTUY 4 (4) 1 (4) AEEINOQSSTUY 3 (3) 0 (3) (2) (2) (1) (1) razem 63 (66) 31 (66) 103

Sortowanie bąbelkowe czas wykonania Zależność od danych wejściowych: Ilość potrzebnych przejść: tak Ilość porównań w jednym przejściu: nie Ilość zamian: tak Najlepszy przypadek: O(N) Jeśli elementy są już posortowane, np.: ABCDEFGHIJ. Tylko jedno przejście. Stąd mamy N-1 porównań i 0 zamian. Najgorszy przypadek: O(N 2 ) Dane odwrotnie posortowane, np.: JIHGFEDCBA. N-1 przejść (N-1)N/2 porównań i (N-1)N/2 zamian 104

Sortowanie przez wstawianie pomysł Dla każdego elementu ciągu (od lewej do prawej), wstawiamy go we właściwe miejsce ciągu elementów poprzedzających go (już posortowanych). 105

Sortowanie przez wstawianie pseudokod INSERTION_SORT(A) 1 for j 2 to length[a] 2 do key A[j] 3 i j-1 4 while i>0 and A[i]>key 5 do A[i+1] A[i] 6 i i-1 7 A[i+1] key 106

Sortowanie przez wstawianie przykład iteracja 1 2 3 4 5 6 7 8 9 10 11 Ciąg: EASYQUESTION, (12 znaków) porównań zamian (najgorszy przyp.) EASYQUESTION AESYQUESTION 1 (1) 1 (1) AESYQUESTION 1 (2) 0 (2) AESYQUESTION 1 (3) 0 (3) AEQSYUESTION 3 (4) 2 (4) AEQSUYESTION 2 (5) 1 (5) AEEQSUYSTION 5 (6) 4 (6) AEEQSSUYTION 3 (7) 2 (7) AEEQSSTUYION 3 (8) 2 (8) AEEIQSSTUYON 7 (9) 6 (9) AEEIOQSSTUYN 7 (10) 6 (10) AEEINOQSSTUY 8 (11) 7 (11) razem 41 (66) 31 (66) 107

Sortowanie przez wstawianie czas działania Zależność od danych wejściowych: Ilość iteracji: nie (zawsze N-1) Ilość porównań: tak Ilość zamian: tak Najgorszy przypadek: O(N 2 ) Elementy odwrotnie posortowane np.: JIHGFEDCBA. (N-1)N/2 porównań i (N-1)N/2 zamian. Najlepszy przypadek: O(N) Elementy już posortowane np.: ABCDEFGHIJ. Jedno porównanie i 0 zamian w każdej iteracji. Razem, N-1 porównań i brak zamian. 108

Shellsort pomysł Modyfikacja (rozszerzona wersja) sortowania przez wstawianie Dążymy do zmniejszenia ilości zamian albo ciągi krótkie, albo lepsze ( bliższe posortowania). Shellsort wykonuje sortowanie podciągów: Wybieramy ciąg liczb (zwany ciągiem przyrostów) h t,, h 2, h 1 ; h 1 =1; h t > > h 2 >h 1 ; Sortujemy ciągi elementów odległych o h t, h t-1, h t-2,,h 1. 109

Shellsort kod w C void shellsort (int[ ] a, int n) { int i, j, k, h, v; int[ ] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1} for (k=0; k<16; k++) { h=cols[k]; for (i=h; i<n; i++) { v=a[i]; j=i; while (j>=h && a[j-h]>v) { a[j]=a[j-h]; j=j-h; } a[j]=v; } } } 110

Shellsort przykład ciąg: EASYQUESTION (12 znaków) ciąg przyrostów 4, 1. faza 1: przyrost = 4 porównań zamian EASYQUESTION EASYQUESTION 2 0 EASYQIESTUON 3 1 EAEYQIOSTUSN 3 2 EAENQIOSTUSY 3 3 Razem w tej fazie 11 6 111

Shellsort przykład faza 2: przyrost= 1 porównań zamian EAENQIOSTUSY AEENQIOSTUSY 1 1 AEENQIOSTUSY 1 0 AEENQIOSTUSY 1 0 AEENQIOSTUSY 1 0 AEEINQOSTUSY 3 2 AEEINOQSTUSY 2 1 AEEINOQSTUSY 1 0 AEEINOQSTUSY 1 0 AEEINOQSTUSY 1 0 AEEINOQSSTUY 3 2 AEEINOQSSTUY 1 0 Razem w tej fazie 16 6 112

Shellsort przykład Razem 27 porównań i 12 zamian. Dla sortowania przez wstawiania odpowiednio 41 i 31!!! Polepszenie dostajemy przez wstępne posortowanie, krótkich podciągów Zwykle stosuje się ciągi przyrostów o więcej niż 2 elementach. 113

Shellsort czas działania Nie ma możliwości przeprowadzenie dokładnej analizy dla przypadki ogólnego (wyniki są oparte o badania empiryczne). Wybór ciągu przyrostów ma zasadniczy wpływ na czas sortowania. Dla ciągu podanego przez Shell a: O(N 2 ) I max = Floor(N/2), I k = Floor(I k+1 /2). np N=64:1, 2, 4, 8, 16, 32 Dla ciągu podanego przez Knuth a: O(N 3/2 ) I 1 =1, I k+1 = 1+3*I k. 1, 4, 13, 40, 121, 364, 114

Mergesort pomysł Dzielimy ciąg na podciągi, sortujemy te podciągi, a następnie łączymy zachowując porządek. Przykład algorytmu typu dziel i zwyciężaj. Potrzeba dodatkowego miejsca dla tych podciągów nie jest to sortowanie w miejscu. Można realizować ten proces w miejscu, ale rośnie stopień komplikacji. 115

Mergesort przykład ciąg: EASYQUESTION (12 znaków) podział EASYQUESTION EASYQU ESTION EAS YQU EST ION E AS Y QU E ST I ON A S Q U S T O N 116

Mergesort przykład łaczenie AEEINOQSSTUY A E S AEQSUY EINOST C1 AES QUY EST INO E AS Y QU E ST I NO A S Q U S T O N Q U Y C2 C3 117

Mergesort - pseudokod MERGE-SORT(A, p, r) 1 if p < r 2 then q (p + r)/2 3 MERGE-SORT(A, p, q) 4 MERGE-SORT(A, q + 1, r) 5 MERGE(A, p, q, r) 118

Mergesort - pseudokod MERGE(A, p, q, r) 1 n1 q - p + 1 2 n2 r - q 3 create arrays L[1..n1 + 1] and R[1..n2 + 1] 4 for i 1 to n1 5 do L[i] A[p + i - 1] 6 for j 1 to n2 7 do R[j] A[q + j] 8 L[n1 + 1] 9 R[n2 + 1] 10 i 1 11 j 1 12 for k p to r 13 do if L[i] R[j] 14 then A[k] L[i] 15 i i + 1 16 else A[k] R[j] 17 j j + 1 119

Sortowanie w czasie liniowym 120

Przegląd Czy możliwe jest sortowanie w czasie lepszym niż dla metod porównujących elementy (poprzednio najlepsze algorytmy dawały czas O(NlogN))? Algorytmy o liniowym czasie działania: Przez zliczanie (Counting-Sort) Radix-Sort Kubełkowe (Bucket-sort) Potrzeba dodatkowych założeń! 121

Sortowanie o czasie liniowym Możliwe przy dodatkowych informacjach (założeniach) o danych wejściowych. Przykłady takich założeń: Dane są liczbami całkowitymi z przedziału [0..k] i k = O(n). Dane są liczbami wymiernymi z przedziału [0,1) o rozkładzie jednostajnym na tym przedziale Trzy algorytmy: Counting-Sort Radix-Sort Bucket-Sort 122

Zliczanie (Counting sort) wejście: n liczb całkowitych z przedziału [0..k], dla k = O(n). pomysł: dla każdego elementu wejścia x określamy jego pozycje (rank): ilość elementów mniejszych od x. jeśli znamy pozycję elementu umieszczamy go na r+1 miejscu ciągu przykład: jeśli wiemy, że w ciągu jest 6 elementów mniejszych od 17, to 17 znajdzie się na 7 miejscu w ciągu wynikowym. powtórzenia: jeśli mamy kilka równych elementów umieszczamy je kolejno poczynając od indeksu pozycja 123

Zliczanie (Counting sort) A = Rank = 4 2 1 3 5 1 2 3 4 5 Dla każdego A[i], liczymy elementy od niego. Daje to rank (pozycję) elementu B = 1 2 3 4 5 Jeśli nie ma powtórzeń i n = k, Rank[A[i]] = A[i] i B[Rank[A[i]] A[i] 124

Zliczanie (Counting sort) A = 5 2 1 3 Jeśli nie ma powtórzeń i n < k, Rank = 1 2 3 4 B = 1 2 3 5 Niektóre komórki tablicy rank pozostają niewykorzystane, ale algorytm działa. 125

Zliczanie (Counting sort) A = 4 2 1 2 3 Jeśli n > k i mamy powtórzenia, Rank = 1 32 4 5 B = 1 2 2 3 4 umieszczamy na wyjściu powtarzające się elementy w takiej kolejności, w jakiej występowały w oryginalnym ciągu (stabilność) 126

Zliczanie (Counting sort) A[1..n] tablica wejściowa B [1..n] tablica wyjściowa C [0..k] pomocnicza tablica (do zliczania) Counting-Sort(A, B, k) 1. for i 0 to k 2. do C[i] 0 3. for j 1 to length[a] 4. do C[A[j]] C[A[j]] +1 5. /* C zawiera ilości elementów równych i 6. for i 1 to k 7. do C[i] C[i] + C[i 1] 8. /* C zawiera ilości elementów i 9. for j length[a] downto 1 10. do B[C[A[j]]] A[j] 11. C[A[j]] C[A[j]] 1 127

Sortowanie przez zliczanie przykład (1) A = C = C = 1 2 3 4 5 6 7 8 2 5 3 0 2 3 0 3 0 1 2 3 4 5 2 0 2 3 0 1 0 1 2 3 4 5 2 2 4 7 7 8 1 2 3 4 5 6 7 8 B = 3 C = 0 1 2 3 4 5 2 2 4 76 7 8 n = 8 k = 6 C[A[j]] C[A[j]] +1 po p.4 C[i] C[i] + C[i 1] po p. 7 B[C[A[j]]] A[j] C[A[j]] C[A[j]] 1 po p. 11 128

Sortowanie przez zliczanie przykład (2) A = C = 1 2 3 4 5 6 7 8 2 5 3 0 2 3 0 3 0 1 2 3 4 5 2 2 4 6 7 8 1 2 3 4 5 6 7 8 B = 0 3 0 1 2 3 4 5 C = 21 2 4 6 7 8 129

Sortowanie przez zliczanie przykład (3) A = C = B = C = 1 2 3 4 5 6 7 8 2 5 3 0 2 3 0 3 0 1 2 3 4 5 2 2 4 6 7 8 1 2 3 4 5 6 7 8 0 3 3 0 1 2 3 4 5 1 2 4 65 7 8 130

Counting sort czas działania Pętla for w p.1-2 zajmuje czas Θ(k) Pętla for w p.3-4 zajmuje czasθ(n) Pętla for w p.6-7 zajmuje czasθ(k) Pętla for w p.9-11 zajmuje czasθ(n) Stąd dostajemy łączny czas Θ(n+k) Ponieważ k = O(n), T(n) = Θ(n) algorytm jest optymalny!! Konieczne jest założenie k = O(n). Jeśli k >> n to potrzeba to potrzeba dużej ilości pamięci. Nie jest to sortowanie w miejscu. 131

Radix sort sortowanie pozycyjne wejście: n liczb całkowitych, d-cyfrowych, łańcuchów o d-pozycjach pomysł: zajmować się tylko jedną z cyfr (sortować względem kolejnych pozycji cyfr/znaków). Zaczynamy od najmniej znaczącej cyfry/znaku, potem kolejne pozycje (cyfry/znaki), aż do najbardziej znaczącej. Musimy stosować metodą stabilną. Ponieważ zbiór możliwych wartości jest mały (cyfry 0-9, znaki a - z ) możemy zastosować metodę zliczania, o czasie O(n) Po zakończeniu ciąg będzie posortowany!! 132

Radix sort przykład 3 2 9 7 2 0 7 2 0 3 2 9 4 5 7 3 5 5 3 2 9 3 5 5 6 5 7 4 3 6 4 3 6 4 3 6 8 3 9 4 5 7 8 3 9 4 5 7 4 3 6 6 5 7 3 5 5 6 5 7 7 2 0 3 2 9 4 5 7 7 2 0 3 5 5 8 3 9 6 5 7 8 3 9 133

Radix-Sort pseudokod Radix-Sort(A, d) 1. for i 1 to d 2. do zastosuj stabilną metodę sortowania do cyfry d dla tablicy A uwagi: złożoność: T(n) = Θ(d(n+k)) Θ(n) dla stałego d i k = O(1) wartości cyfr/znaków są z zakresu [0..k 1] dla k = O(1) Metoda stosowana dla poszczególnych pozycji musi być stabilna! 134

Sortowanie kubełkowe Bucket sort wejście: n liczb rzeczywistych z przedziału [0..1) ważne jest, aby były równomiernie rozłożone (każda wartość równie prawdopodobna) pomysł: dzielimy przedział [0..1) na n podprzedziałów ( kubełków ):0, 1/n, 2/n. (n 1)/n. Elementy do odpowiednich kubełków, a i : 1/i a i 1/(i+1). Ponieważ rozkład jest równomierny to w żadnym z przedziałów nie powinno znaleźć się zbyt wiele wartości. Jeśli wkładamy je do kubełków zachowując porządek (np. przez wstawianie Insertion-Sort), dostaniemy posortowany ciąg. 135

Bucket sort przykład.78 0.12.17 1.12.17.17.39 2.21.23.26.21.26 3.39.23.72 4.26.94 5.39. 21 6.68.68.12 7.72.78.72.23 8.78.68 9.94.94 136

Bucket-Sort A[i] tablica wejściowa B[0], B[1], B[n 1] lista kubełków Bucket-Sort(A) 1. n length(a) 2. for i 0 to n 3. do wstaw A[i] do listy B[floor(nA[i])] 4. for i 0 to n 1 5. do Insertion-Sort(B[i]) 6. Połącz listy B[0], B[1], B[n 1] 137

Bucket-Sort złożoność czasowa Wszystkie instrukcje z wyjątkiem 5 (Insertion-Sort) wymagają czasu O(n), w przypadku pesymistycznym. W przypadku pesymistycznym, O(n) liczb trafi do jednego kubełka czyli ich sortowanie zajmie czas O(n 2 ). Jednak w średnim przypadku stała ilość elementów wpada do jednego przedziału stąd czas średni wyniesie O(n). 138

Sortowanie Quicksort i Heapsort 139

Sortowanie - zadanie Definicja (dla liczb): wejście: ciąg n liczb A = (a 1, a 2,, a n ) wyjście: permutacja (a 1,, a n ) taka, że a 1 a n 140

Zestawienie czasów działania Przez wybór: O(N 2 ) zawsze Bąbelkowe: O(N 2 ) najgorszy przypadek; O(N) najlepszy przyp. Wstawianie: O(N 2 ) średnio; O(N) najlepszy przypadek Shellsort: O(N 4/3 ) Heapsort: O(NlogN) zawsze Mergesort: O(NlogN) zawsze Quicksort: O(NlogN) średnio; O(N 2 ) najgorszy przypadek Zliczanie: O(N) zawsze Radix sort: O(N) zawsze zewnętrzne: O(b logb)) dla pliku o b stronach. 141

Dzisiaj: Dwa algorytmy sortowania: Quicksort Bardzo popularny algorytm, bardzo szybki w średnim przypadku Heapsort Wykorzystuje strukturę kopca (heap) 142

Sortowanie szybkie (Quick Sort) - pomysł Jest to najszybszy w praktyce algorytm sortowania, pozwala na efektywne implementacje. średnio: O(NlogN) najgorzej O(N 2 ), przypadek bardzo mało prawdopodobny. Procedura: Wybieramy element osiowy (pivot ). Dzielimy ciąg na dwa podciągi: elementów mniejszych lub równych od osiowego oraz elementów większych od osiowego. Powtarzamy takie postępowanie, aż osiągniemy ciąg o długości 1. Algorytm typu dziel i zwyciężaj. Jest to metoda sortowania w miejscu (podobnie jak Insert-sort, przeciwnie do np. Merge-sort), czyli nie wymaga dodatkowej pamięci 143

Quicksort algorytm QUICKSORT(A, p, r) 1 if p < r 2 then q PARTITION(A, p, r) 3 QUICKSORT(A, p, q - 1) 4 QUICKSORT(A, q + 1, r) Problemy: 1. Wybór elementu osiowego; 2. Podział (partition). 144

Quicksort podział Funkcja partition dzieli ciąg na dwa podciągi: elementów mniejszych (bądź równych) od osiowego i większych od niego Po podziale: {a[j] a[j] <= a[i] dla j [left, i-1]} a[i] El. osiowy {a[k] a[k] > a[i] dla k [i+1,right]} wynik quicksort(a, left, i-1) wynik quicksort(a, i+1, right) 145

Quicksort przykład podziału ciąg: EASYQUESTION (12 znaków). El. osiowy: N Przeglądaj aż: a[i] > a[right] Przeglądaj aż: a[j] <= a[right] E A S Y Q U E S T I O N i E A I Y Q U E S T S O N i j E A I E Q U Y S T S O N i j j Swap(a[i], a[j]) Swap(a[i], a[j]) (indeksy i oraz j minęły się) Swap(a[i], a[right]) E A I E N U Y S T S O Q Lewy podciąg Prawy podciąg 146

Quicksort wybór elementu osiowego opcja 1: zawsze wybierać skrajny element (pierwszy lub ostatni). Zalety: szybkość; Wady: jeśli trafimy na najmniejszy (największy) element podział nie redukuje istotnie problemu. opcja 2: wybieramy losowo. Zalety: średnio powinno działać dobrze (podział na podciągi o zbliżonej długości); Wady: czasochłonne i nie gwarantuje sukcesu. opcja 3: wybieramy medianę z pierwszych/ostatnich/środkowych 3/5/7 elementów. gwarantuje, że nie będzie zdegenerowanych podciągów (pustych). kompromis pomiędzy opcją 1 i 2 147

Podział pseudokod (opcja 1) Partition(A, Left, Right) 1. Pivot A[Right] 2. i Left 1 3. for j Left to Right 1 4. do if (A[j] Pivot) 5. then i i + 1 6. Exchange(A[i], A[j]) 7. Exchange (A[i+1], A[Right]) 8. return i +1 148

Randomizowany Quicksort (opcja 2) Zakładamy że nie ma powtórzeń Jako element osiowy wybieramy losowy element ciągu (opcja 2) Powtarzamy procedurę, wszystkie podziały są równie prawdopodobne (1:n-1, 2:n-2,..., n-1:1), z prawdopodobieństwem 1/n Randomizacja jest drogą do unikania najgorszego przypadku 149

Randomizowany Quicksort Randomized-Partition(A,p,r) 01 i Random(p,r) 02 exchange A[r] A[i] 03 return Partition(A,p,r) Randomized-Quicksort(A,p,r) 01 if p<r then 02 q Randomized-Partition(A,p,r) 03 Randomized-Quicksort(A,p,q) 04 Randomized-Quicksort(A,q+1,r) 150

Quicksort czas działania Najgorszy przypadek: O(N 2 ) Podciągi zawsze mają długości 0 i N-1 (el. Osiowy jest zawsze najmniejszy/największy). Np. dla posortowanego ciągu i pierwszej opcji wyboru el. osiowego. Najlepszy przypadek: O(NlogN) Podział jest zawsze najlepszy (N/2). El. osiowy zawsze jest medianą. Średnio: O(NlogN) 151

Quicksort najlepszy przypadek Podciągi otrzymane w wyniku podziału są równe T ( n) = 2 T ( n / 2) + Θ( n) 152

Quicksort najgorszy przypadek 153

Quicksort- czas działania T(N) = T(i) + T(N-i-1) + N for N > 1 T(0) = T(1) = 1 T(i) i T(N-i-1) dla podziału i/n-i-1. N dla podziału 1/N-1(liniowe przeglądamy wszystkie elementy). 154

Quicksort czas działania najgorzej: T(N) = T(0) + T(N-1) + N = T(N-1) + N = O(N 2 ) najlepiej: T(N) = 2T(N/2) + N = O(NlogN) średnio : T(N) = (1/N) i=0 N-1 T(i) + (1/N) i=0 N-1 T(N-i-1) + N = (2/N) j=0 N-1 T(j) + N = O(NlogN) 155

Quicksort - uwagi Małe ciągi Quicksort zachowuje sięźle dla krótkich ciągów. Poprawa jeśli podciąg jest mały zastosować sortowanie przez wstawianie (zwykle dla ciągów o długości 5 ~ 20) Porównanie z mergesort: Oba zbudowane na zasadzie dziel i zwyciężaj. Mergesort wykonuje sortowanie w fazie łączenia. Quicksort wykonuje prace w fazie podziału. 156

Heap Sort pojęcie kopca Struktura kopca binarnego Drzewo binarne (bliskie zrównoważenia) Wszystkie poziomy, z wyjątkiem co najwyżej ostatniego, kompletnie zapełnione Wartość klucza w węźle jest większa lub równa od wartości kluczy wszystkich dzieci; własność taka jest zachowana dla lewego i prawego poddrzewa (zawsze) 157

Heap Sort reprezentacja tablicowa kopca Parent (i) return i/2 Left (i) return 2i Right (i) return 2i+1 Własność kopca: 1 2 3 4 5 6 7 8 9 10 A[Parent(i)] A[i] 16 15 10 8 7 9 3 2 4 1 poziomy: 3 2 1 0 158

Heap Sort reprezentacja kopca w tablicy Zauważmy połączenia w drzewie dzieci węzła i występują na pozycjach 2i oraz 2i+1 Czemu to jest wygodne? Dla reprezentacji binarnej, dzieleniu/mnożeniu przez 2 odpowiada przesuwanie (szybka operacja) Dodawanie jedynki oznacza zmianę najmłodszego bitu (po przesunięciu) 159

Kopcowanie (Heapify) Niech i będzie indeksem w tablicy A Niech drzewa binarne Left(i) i Right(i) będą kopcami Ale, A[i] może być mniejsze od swoich dzieci co powoduje złamanie własności kopca Metoda Kopcowania (Heapify) przywraca własności kopca dla A poprzez przesuwanie A[i] w dół kopca aż do momentu, kiedy własność kopca jest już spełniona 160

Kopcowanie (Heapify) 161

Kopcowanie (Heapify) przykład 162

Kopcowanie czas działania Czas działania procedury Heapify dla poddrzewa o n węzłach i korzeniu w i: Ustalenie relacji pomiędzy elementami: Θ(1) dodajemy czas działania Heapify dla poddrzewa o korzeniu w jednym z potomków i, gdzie rozmiar tego poddrzewa 2n/3 jest najgorszym przypadkiem. T ( n) T (2 n /3) + Θ(1) T ( n) = O(log n) Inaczej mówiąc Czas działania dla drzewa o wysokości h: O(h) 163

Budowa kopca Konwertujemy tablicę A[1...n], gdzie n = length[a], na kopiec Zauważmy, że elementy w A[( n/2 + 1)...n] są już zbiorem kopców - jednoelementowych! 164

Budowanie kopca 1 165

Budowanie kopca 2 166

Budowanie kopca 3 167

Budowa kopca analiza Poprawność: indukcja po i, (wszystkie drzewa o korzeniach m > i są kopcami) Czas działania: n wywołań kopcowania (Heapify) = n O(lg n) = O(n lg n) Wystarczająco dobre ograniczenie O(n lg n) dla zadania sortowanie (Heapsort), ale czasem kopiec budujemy dla innych celów 168

Sortowanie za pomocą kopca Heap Sort O( n) Czas działania O(n lg n) + czas budowy kopca (O(n)) 169

Heap Sort 1 170

Heap Sort 2 171

Heap Sort podsumowanie Heap sort wykorzystuje strukturę kopca przez co dostajemy asymptotycznie optymalny czas sortowania Czas działania O(n log n) podobnie do merge sort, i lepiej niż wybór, wstawianie czy bąbelkowe Sortowanie w miejscu podobnie do sortowania przez wybór, wstawianie czy bąbelkowego 172

Dynamiczne struktury danych 173

Plan wykładu Wprowadzenie Popularne dynamiczne struktury danych (ADT) stosy, kolejki, listy opis abstrakcyjny Listy liniowe Implementacja tablicowa stosu i kolejki Drzewa Możliwe implementacje 174

Wprowadzenie Do tej pory najczęściej zajmowaliśmy się jedną strukturą danych tablicą. Struktura taka ma charakter statyczny jej rozmiar jest niezmienny. Powoduje to konieczność poznania wymaganego rozmiaru przed rozpoczęciem działań (ewentualnie straty miejsca deklarujemy wystarczająco dużą tablicę). W wielu zadaniach wygodniejsza jest struktura o zmiennym rozmiarze (w zależności od aktualnych potrzeb) struktura dynamiczna. Potrzebujemy struktury pozwalającej na przechowywanie elementów niezależnie od ich fizycznego położenia. logicznie fizycznie 2 0 5 3 4 1 3 2 4 1 0 5 175

Wprowadzenie Przykładowe operacje dla struktur danych: Insert(S, k): wstawianie nowego elementu Delete(S, k): usuwanie elementu Min(S), Max(S): odnajdowanie najmniejszego/największego elementu Successor(S,x), Predecessor(S,x): odnajdowanie następnego/poprzedniego elementu Zwykle przynajmniej jedna z tych operacji jest kosztowna czasowo (zajmuje czas O(n)). Czy można lepiej? 176

Abstrakcyjne typy danych (Abstract Data Types ADT ) Abstrakcyjnym typem danych nazywany formalną specyfikację sposobu przechowywania obiektów oraz zbiór dobrze opisanych operacji na tych obiektach. Jaka jest różnica pomiędzy strukturą danych a ADT? struktura danych (klasa) jest implementacją ADT dla specyficznego komputera i systemu operacyjnego. 177

Popularne dynamiczne ADT Listy łączone Stosy, kolejki Drzewa z korzeniem (rooted trees), binarne, BST, czerwonoczarne, AVL itd. Kopce i kolejki priorytetowe (późniejsze wykłady) Tablice z haszowaniem (późniejsze wykłady) 178

Listy Lista L jest liniową sekwencją elementów. Pierwszy element listy jest nazywany head, ostatni tail. Jeśli obydwa są równe null, to lista jest pusta Każdy element ma poprzednik i następnik (za wyjątkiem head i tail) Operacje na liście: Successor(L,x), Predecessor(L,x) List-Insert(L,x) List-Delete(L,x) List-Search(L,k) 2 0 2 3 0 1 head x tail 179

Listy łączone Rozmieszczenie fizyczne obiektów w pamięci nie musi odpowiadać ich logicznej kolejności; wykorzystujemy wskaźniki do obiektów (do następnego/poprzedniego obiektu) Manipulując wskaźnikami możemy dodawać, usuwać elementy do listy bez przemieszczania pozostałych elementów listy Lista taka może być pojedynczo lub podwójnie łączona. head a1 a2 a3 an tail null null 180

Węzły i wskaźniki Węzłem nazywać będziemy obiekt przechowujący daną oraz wskaźnik do następnej danej i (opcjonalnie dla listy podwójnie łączonej) wskaźnik do poprzedniej danej. Jeśli nie istnieje następny obiekt to wartość wskaźnika będzie null Wskaźnik oznacza adres obiektu w pamięci Węzły zajmują zwykle przestrzeń: Θ(1) struct node { } key_type key; data_type data; struct node *next; struct node *prev; key data next prev 181

Wstawianie do listy (przykład operacji na liście) wstawianie nowego węzła q pomiędzy węzły p i r: p r p q r a1 a3 a1 a2 a3 a2 next[q] r next[p] q 182

Usuwanie z listy usuwanie węzła q p q r p r a1 a2 a3 a1 a3 next[p] r next[q] null a2 q null 183

Operacje na liście łączonej List-Search(L, k) 1. x head[l] 2. while x null and key[x] k 3. do x next[x] 4. return x List-Insert(L, x) 1. next[x] head[l] 2. if head[l] null 3. then prev[head[l]] x 4. head[l] x 5. prev[x] null List-Delete(L, x) 1. if prev[l] null 2. then next[prev[x]] next[x] 3. else head[l] next[x] 4. if next[l] null 5. then prev[next[x]] prev[x] 184

Listy podwójnie łączone head x a1 a2 a3 a4 tail null null Listy cykliczne: łączymy element pierwszy z ostatnim 185

Stosy Stosem S nazywany liniową sekwencję elementów do której nowy element x może zostać wstawiony jedynie na początek, analogicznie element może zostać usunięty jedynie z początku tej sekwencji. Stos rządzi się zasadą Last-In-First-Out (LIFO). Operacje dla stosu: Stack-Empty(S) Pop(S) Push(S,x) Push 2 0 1 5 Pop head null 186

Kolejki Kolejka Q jest to liniowa sekwencja elementów do której nowe elementy wstawiane są na końcu sekwencji, natomiast elementy usuwane są z jej początku. Zasada First-In-First-Out (FIFO). Operacje dla kolejki: Queue-Empty(Q) EnQueue(Q, x) DeQueue(Q) DeQueue EnQueue 2 0 2 3 0 1 head tail 187

Implementacja stosu i kolejki Tablicowa Wykorzystujemy tablicę A o n elementach A[i], gdzie n jest maksymalną ilością elementów stosu/kolejki. Top(A), Head(A) i Tail(A) są indeksami tablicy Operacje na stosie/w kolejce odnoszą się do indeksów tablicy i elementów tablicy Implementacja tablicowa nie jest efektywna Listy łączone Nowe węzły tworzone są w miarę potrzeby Nie musimy znać maksymalnej ilości elementów z góry Operacje są manipulacjami na wskaźnikach 188

Implementacja tablicowa stosu Push(S, x) 1. if top[s] = length[s] 2. then error overflow 3. top[s] top[s] + 1 4. S[top[S]] x 0 1 2 3 4 5 6 1 5 2 3 Pop(S) 1. if top[s] = -1 2. then error underflow 3. else top[s] top[s] 1 4. return S[top[S] +1] top Kierunek wstawiania Stack-Empty(S) 1. if top[s] = -1 2. then return true 3. else return false 189

Implementacja tablicowa kolejki Dequeue(Q) 1. x Q[head[Q]] 2. if head[q] = length[q] 3. then head[q] 1 4. else head[q] (head[q]+1) mod n 5. return x 1 5 2 3 0 tail head Enqueue(Q, x) 1. Q[tail[Q]] x 2. if tail[q] = length[q] 3. then tail[q] x 4. else tail[q] (tail[q]+1) mod n 190

Drzewa z korzeniem Drzewem z korzeniem T nazywamy ADT dla którego elementy są zorganizowane w strukturę drzewiastą. Drzewo składa się z węzłów przechowujących obiekt oraz krawędzi reprezentujących zależności pomiędzy węzłami. W drzewie występują trzy typy węzłów: korzeń (root), węzły wewnętrzne, liście Własności drzew: Istnieje droga z korzenia do każdego węzła (połączenia) Droga taka jest dokładnie jedna (brak cykli) Każdy węzeł z wyjątkiem korzenia posiada rodzica (przodka) Liście nie mają potomków Węzły wewnętrzne mają jednego lub więcej potomków (= 2 binarne) 191

Drzewa z korzeniem A 0 B C D 1 E F G H I J 2 K L M N 3 192

Terminologia Rodzice (przodkowie) i dzieci (potomkowie) Rodzeństwo (sibling) potomkowie tego samego węzła Relacja jest dzieckiem/rodzicem. Poziom węzła Ścieżka (path): sekwencja węzłów n 1, n 2,,n k takich, że n i jest przodkiem n i+1. Długościąścieżki nazywamy liczbę k. Wysokość drzewa: maksymalna długośćścieżki w drzewie od korzenia do liścia. Głębokość węzła: długośćścieżki od korzenia do tego węzła. 193

Drzewa binarne Drzewem binarnym T nazywamy drzewo z korzeniem, dla którego każdy węzeł ma co najwyżej 2 potomków. A B C A B C D E F D E F G Porządek węzłów jest istotny!!! G 194

Drzewa pełne i drzewa kompletne Drzewo binarne jest pełne jeśli każdy węzeł wewnętrzny ma dokładnie dwóch potomków. Drzewo jest kompletne jeśli każdy liść ma tę samą głębokość. A A B C B C D E D E F G F G pełne kompletne 195

Własności drzew binarnych Ilość węzłów na poziomie d w kompletnym drzewie binarnym wynosi 2 d Ilość węzłów wewnętrznych w takim drzewie: 1+2+4+ +2 d 1 = 2 d 1 (mniej niż połowa!) Ilość wszystkich węzłów: 1+2+4+ +2 d = 2 d+1 1 Jak wysokie może być drzewo binarne o n liściach: (n 1)/2 Wysokość drzewa: 2 d+1 1= n log (n+1) 1 log (n) 196

Tablicowa implementacja drzewa binarnego 1 A 2 3 B C 4 5 6 7 D E F G Poziom 0 1 2 Na każdym poziomie d mamy 2 d elementów 1 2 3 4 5 6 7 A B C D E F G 2 0 2 1 2 2 Kompletne drzewo: parent(i) = floor(i/2) left-child(i) = 2i right-child(i) = 2i +1 197

Listowa implementacja drzewa binarnego root(t) A B C Każdy węzeł zawiera Dane oraz 3 wskaźniki: przodek lewy potomek prawy potomek D E F G H data 198

Listowa implementacja drzewa binarnego (najprostsza) root(t) A B C Każdy węzeł zawiera Dane oraz 2 wskaźniki: lewy potomek prawy potomek D E F G H data 199

Listowa implementacja drzewa (n-drzewa) root(t) A D B E C D F G H I Każdy węzeł zawiera Dane oraz 3 wskaźniki: przodek lewy potomek prawe rodzeństwo J K 200

Drzewa poszukiwań binarnych (BST) 201

O czym będziemy mówić Definicja Operacje na drzewach BST: Search Minimum, Maximum Predecessor, Successor Insert, Delete Struktura losowo budowanych drzew BST 202

Wprowadzenie Poszukujemy dynamicznego ADT, który efektywnie będzie obsługiwał następujące operacje: Wyszukiwanie elementu (Search) Znajdowanie Minimum/Maximum Znajdowanie poprzednika/następnika (Predecessor/Successor) Wstawianie/usuwanie elementu (Insert/Delete) Wykorzystamy drzewo binarne! Wszystkie operacje powinny zajmować czasθ(lg n) Drzewo powinno być zawsze zbalansowane inaczej czas będzie proporcjonalny do wysokości drzewa (gorszy od O(lg n))! 203

Drzewo poszukiwań binarnych (binary search tree) Struktura drzewa z korzeniem Każdy węzeł x posiada pola left(x), right(x), parent(x), oraz key(x). Własność drzewa BST: Niech x będzie dowolnym węzłem drzewa, natomiast niech y będzie należał do poddrzewa o korzeniu w x wtedy:. Jeżeli należy do lewego poddrzewa to: key(y) key(x) Jeżeli należy do prawego poddrzewa to : key(y) > key(x) 204

Przykład BST 5 2 3 7 3 2 5 8 7 5 8 5 Metody przechodzenia przez drzewo : In-order, pre-order, post-order 205

Poszukiwanie w drzewie BST Tree-Search(x,k) if x = null or k = key[x] then return x if k < key[x] then return Tree-Search(left[x],k) else return Tree-Search(right[x],k) rekurencyjnie Tree-Search(x,k) while x null and k key[x] do if k < key[x] then x left[x] else x right[x] return x iteracyjnie złożoność: O(h) 206

Przykład Poszukiwany klucz: 13 207

Przechodzenie przez wszystkie węzły drzewa Inorder-Tree-Walk(x) if x null then Inorder-Tree-Walk(left[x]) print key[x] Inorder-Tree-Walk(right[x]) czas wykonania: T(0) = Θ(1) T(n)=T(k) + T(n k 1) + Θ(1) złożoność: Θ(n) 208

Odnajdowanie minimum i maksimum Tree-Minimum(x) while left[x] null do x left[x] return x Tree-Maximum(x) while right[x] null do x right[x] return x złożoność: O(h) 209

Przykład minimum 210

Odnajdowanie następnika Następnikiem x nazywamy najmniejszy element y wśród elementów większych od x Następnik może zostać odnaleziony bez porównywania kluczy. Jest to : 1. null jeśli x jest największym z węzłów. 2. minimum w prawym poddrzewie t jeśli ono istnieje. 3. najmniejszy z przodków x, dla których lewy potomek jest przodkiem x. 211

Odnajdowanie następnika x z y minimum w prawym poddrzewie t x najmniejszy z przodków x, dla których lewy potomek jest przodkiem x 212

Odnajdowanie następnika Tree-Successor(x) if right[x] null // przypadek 2 then return Tree-Minimum(right[x]) y parent[x] while y null and x = right[y] // przypadek 3 do x y y parent[y] return y 213

Przykład Poszukajmy następników dla 15 (przyp. 2), 13 (przyp. 3) 214

Wstawianie elementów Wstawianie jest bardzo zbliżone do odnajdowania elementu: Odnajdujemy właściwe miejsce w drzewie, w które chcemy wstawić nowy węzeł z. Dodawany węzeł z zawsze staje się liściem. Zakładamy, że początkowo left(z) oraz right(z) mają wartość null. 215

Wstawanie przykład Wstawiamy 13 do drzewa 12 5 18 2 9 15 19 z 13 17 216

Wstawianie pseudokod Tree-Insert(T,z) y null x root[t] while x null do y x if key[z] < key[x] then x left[x] else x right[x] parent[z] y // dla pustego drzewa if y = null then root[t] z else if key[z] < key[y] then left[y] z else right[y] z 217

Usuwanie z drzewa BST Usuwanie elementu jest bardziej skomplikowane niż wstawianie. Można rozważać trzy przypadki usuwania węzła z: 1. z nie ma potomków 2. z ma jednego potomka 3. z ma 2 potomków przypadek 1: usuwamy z i zmieniamy u rodzica wskazanie na null. przypadek 2: usuwamy z a jego dziecko staje się dzieckiem rodzica. przypadek 3:najbardziej złożony; nie można po prostu usunąć węzła i przenieść dzieci do rodzica drzewo przestałoby być binarne! 218

Usuwanie z drzewa BST - przypadek 1 usuwamy delete 219

Usuwanie z drzewa BST przypadek 2 Usuwany węzeł 220

Usuwanie z drzewa BST przypadek 3 Rozwiązanie polega na zastąpieniu węzła jego następnikiem. założenie: jeśli węzeł ma dwóch potomków, jego następnik ma co najwyżej jednego potomka. dowód: jeśli węzeł ma 2 potomków to jego następnikiem jest minimum z prawego poddrzewa. Minimum nie może posiadać lewego potomka (inaczej nie byłoby to minimum) 221

Usuwanie z drzewa BST przypadek 3 Usuwamy z z δ y δ α β α β y w w 222

Usuwanie z drzewa BST przypadek 3 usuwamy następnik 223