Podstawowe Pojęcia Przy Badaniu Algorytmów I Struktur Danych Analiza algorytmiczna: to dział informatyki zajmujący się szukaniem najlepszych algorytmów dla zadań komputerowych. - odpowiada na pytania: * czy dany problem może być rozwiązany na komputerze dostępnym czasie i pamięci? * który algorytm zastosować a danych okolicznościach? * czy jest lepszy algorytm? (czy jest on optymalny?). * jak uzasadnić, że algorytm rozwiązuje postawione zadanie? - prowadzona jest w dwóch aspektach poprawności semantycznej i złożoności obliczeniowej (również w strukturach danych) - uwzględnia poprawność semantyczną, prostotę, czas działania, zajętą pamięć, optymalność i okoliczność użycia. Częściowa poprawność:- (jeśli nie to: dla pewnych danych wejściowych obliczenie algorytmu dochodzi do punktu końcowego, ale wyniki są różne od spodziewanych wartości końcowych) Własność określoności obliczeń:- (jeśli nie to: zatrzymuje się w punkcie nie końcowym algorytmu) Własność stopu:- (jeśli nie to: algorytm jest nieskończony (pętla, rekurencja)) Złożoność obliczeniowa: Definiuje się jako – ilość zasobów komputerowych potrzebnych do jego wykonania (czas procesora, zajętość pamięci) Za jednostkę przyjmuje się – wykonanie jednej operacji dominującej. Wyróżnia się: - złożoność pesymistyczną (ilość zasobów komputerowych potrzebnych przy „najgorszych” danych wejściowych o rozmiarze n) - złożoność oczekiwaną (ilość zasobów komputerowych potrzebnych przy „typowych” danych wejściowych o rozmiarze n) - miary wrażliwości algorytmów pesymistyczną i oczekiwaną. Analiza Probabilistyczna: - koszt algorytmu jako zmienna losowa, właściwości asymptotyczne (poprawne dla dużych danych wejściowych) Typowe złożoności: - Stała. - Logarytmiczna, - Kwadratowo-logarytmiczna, - Liniowa, - Liniowo-logarytmiczna, - Kwadratowa, - Sześcienna, - Wielomianowa c.d. - Wykładnicza – nie realizowalność dla dużych danych. Struktury Danych Dana – zapis informacji na nośniku komputerowym Dana prosta – zapis informacji, którego żadna część nie jest zapisem informacji. Dana złożona – (statyczna struktura danych) składa się z: - Podłoża - Pokrycia tego podłoża - Wyróżnienia działek podłoża Podłoże – struktury statycznej to para: zbiór działek (pozycji) podłoża oraz zbiór relacji (powiązań) między działkami podłoża. Pokrycie – podłoża to para: zbiór danych składowych oraz funkcja przyporządkowująca poszczególnym działkom ich pokrycia (nie musi być różnowartościowa i nie musi być zupełna) Wyróżnienie – działki to podzbiór zbioru działek. Dynamiczne Struktury danych - Dynamiczna struktura danych jest obiektem dynamicznym, którego stan w danej chwili jest strukturą statyczną. Zmianę stanu struktury dynamicznej realizuje się przez: - operacje proste (operacje np. przesunięcia na wyróżnieniach, testujące i modyfikujące strukturę) - operacje złożone (odszukujące działkę, rozmieszczania i pokrycia, reorganizacji struktury np. sortowanie) Wskazanie działki: - Przez adresacje (para: zbiór adresów + funkcja różnowartościowa przypisująca adresom działki), - Przez wyróżnienie (jest podzbiorem działek, najczęściej jednoelementowym) Klasyfikacja Struktur Danych: Ze względu na powiązania podłoża: - Mnogościowe (zbiór relacji (powiązań) jest pusty) - Liniowe (podłoże jest grafem liniowym) - Pierścieniowe (rzadko stosowane) - Drzewiaste - Sieciowe inaczej grafowe (przypadek ogólny – położenie nie pod poprzednie szczególne przypadki). Struktury mnogościowe – zbiory. Zbiór – struktura dynamiczna o pokryciu zupełnym różnowartościowym. Operacje proste: - Sprawdzenie czy jest zbiorem pustym, - Sprawdzenie zbioru na zawartość elementów, - Dodanie elementu do zbioru, - Usunięcie elementu ze zbioru, - Usunięcie całego zbioru, - Podanie elementu następnego. Operacje złożone: - Sumowanie zbiorów, - Iloczyn zbiorów - Różnica zbiorów - Różnica symetryczna rekordów Implementacja: bitowa, tablicowa, listowa. Zastosowania: zbiór znaków, zbiór słów. Struktury liniowe. Podłoże liniowe: - Działki początkowa i końcowa, - Relacje następstwa i poprzednictwa, - Relacje osiągalności naprzód i wstecz. Tablica – jest to struktura dynamiczna o: - Podłożu stałym, - Dostępie adresowym, - Podłożu jednorodnym Tablica liniowa numerowana (TLN) (struktura o podłożu stałym); Operacje proste: - Odczytywanie zawartości działki, - Zmiana zawartości działki. REKORDY Rekordy – rekord jest to struktura mnogościowa o; - Podłożu stałym (jak tablica), - O dostępie adresowym, (ale nie numeruje się działek tylko operuje się nazwami), - O podłożu niekoniecznie jednorodnym (istotna różnica). Tablica rekordów - tablica o działkach pokrytych przez rekordy. Pole kluczowe – (jedno z pól w rekordzie); - Może decydować o położeniu rekordu w tablicy (rozmieszczenie rekordów) - Może być podstawą odszukania. Klasyfikacja rozmieszczenia rekordów w tablicy – według; - gęstości; * luźne, * zwarte - powiązania z zawartością; * chaotyczne, * sterowane; ^ monotoniczne (np. niemalejące, nierosnące), ^ algorytmicznie („haszowanie”), Tablica – operacje złożone; Dla tablicy chaotycznej (zwartej oraz luźnej); - Wprowadzenie rekordu, - Odszukanie rekordu (działek o zadeklarowanym pokryciu) sekwencyjne, krokowe, losowe, - Kasowanie rekordu, - Rozmieszczanie elementów, - Reorganizacja tablicy (np. monotonizacja). Dla tablicy monotonicznej dochodzi; - Odszukanie: binarne (połówkowe), interpolarne, Dla tablicy z rozmieszczeniem algorytmicznym; - Odszukanie algorytmiczne, - Rozmieszczanie algorytmiczne, - Zmiana algorytmu rozmieszczającego. Sortowanie Sortowaniem – nazywamy proces ustawiania obiektów w określonym porządku. Polega na permutowaniu, aż do chwili osiągnięcia ich uporządkowania takiego, że dla danej funkcji porządkującej F; F(ak1) ≤ F(ak2) ≤ … ≤ F(akn) (Wartość tej funkcji nazywa się kluczem). - metodę sortowania nazywamy stabilną, jeżeli podczas procesu pozostaje niezmieniony względny porządek obiektów o identycznych kluczach. - Metody In-situ – wymagania dotyczące pamięci. Klasyfikacja metod sortowania – według; Rodzaju struktury; - Sortowanie tablicy - Sortowanie listy, - Sortowanie łańcucha, Miejsca sortowania; - Wewnętrzne, - Zewnętrzne, Podziału algorytmu na etapy; - Bezpośrednie – (jeden etap, obiekty ulegają przestawieniu), - Pośrednie – dwa etapy; * etap logiczny - informacja jak przestawić obiekty, można wykonać kilka etapów logicznych. * etap fizyczny – nie zawsze konieczny. Wykorzystania pamięci; - Metody intensywne (In situ), - Metody ekstensywne (dodatkowa pamięć, metody szybsze), Efektywności; - Metody proste O(n2), - Metody złożone O(n log n), Stabilności; - Stabilna, - Niestabilna. Sortowania proste: Sortowanie przez proste wstawianie; - Dzielimy ciąg na wynikowy i źródłowy w każdym kroku począwszy od i=2, przenosimy i-ty element ciągu źródłowego do ciągu wynikowego wstawiając go w odpowiednim miejscu. - Zachowanie naturalne, algorytm stabilny, działa w miejscu. - Próba poprawy przez wstawianie połówkowe. - Liczba porównań nie zależy od ustawiania początkowego elementów, faworyzuje nieuporządkowane. - Zmienia się tylko liczba porównań nie przesunięć, czasochłonne, gdy elementy są uporządkowane, gorsze niż liniowe przeszukiwanie. Sortowanie przez proste wybieranie; - Podział też na dwa ciągi, wybieramy element najmniejszy z ciągu źródłowego i wymieniamy go z pierwszym elementem tego ciągu, aż pozostanie w nim jeden element. - Lepszy od prostego wstawiania (mniejsza liczba porównań, gorzej dla elementów posortowanych, niestabilna, działa w miejscu, zalecane dla n<50) Sortowanie przez prostą zamianę (bąbelkowe); - Algorytm polega na porównaniu i ewentualnej zamianie sąsiadujących par ze sobą dopóki wszystkie elementy zostaną posortowane. - Ulepszenia tej metody; * Zapamiętanie, czy dokonano zmian, * Zapamiętanie pozycji ostatniej zmiany i nie sięganie aż do „i”, * Zmiana kierunku przejść (mieszane), * Asymetria ciężkiego i lekkiego końca, korzyści w przypadku prawie posortowanych ciągów elementów, czyli rzadko, nie stosuje się, złożoność n 2. Sortowania złożone: Sortowanie przez podział (szybkie) - Quicksort; - W celu zapewnienia efektywności wymieniamy elementy położone daleko od siebie, - Wersja rekurencyjna i nierekurencyjna, - Warianty; Horanitza, losowy, z medianą 3, kombinacje z metodą prostą (Quickersort), W zależności od wyboru elementu dzielącego; różne działania dla skrajnych przypadków, mediana najmniejsza liczba porównań, mediana z próbki nie wpływa na wydajność, losowy wybór efektywność najmniejsza od optymalnej. Sortowanie grzebieniowe - Combsort; - Pochodzi z 1991 roku, - Oparte na metodzie bąbelkowej, - Włączono tutaj empirię (współczynnik 1.3 wyznaczono doświadczalnie), - Złożoność O(n log n), statyka gorsza niż Quicksort (1,5-2 razy), ale algorytm prosty (bez rekurencji), - Wariant sortowania Combsort; - Podstawowy – za rozpiętość przyjmij długość tablicy, podziel przez 1.3, odrzuć część ułamkową, będzie to pierwsza rozpiętość badanych obiektów, - Combsort 1! – rozpiętość 9 i 10 zastępujemy 11, Sortowanie przez scalanie – Merqesort - Jest to metoda ekstensywna o zapotrzebowaniu na dodatkową pamięć równą pojemność sortowanej tablicy, wyjątkowo dogania Quicksort, statycznie z nim przegrywa, skomplikowany algorytm, dobra idea dla sortowania zewnętrznego, - Algorytm scalania – dane dwa pliki (tablice) monotoniczne, należy scalić w jeden monotoniczny; bierz obiekt z czoła i mniejszy przenieś na plik końcowy, konkatenuj, aż któryś z plików wejściowych się skończy, resztę dopisz na koniec wejściowego pliku, - Wersja podstawowa (iteracyjna); podziel tablice na odcinki 1 elementowe, scalaj 1 z 2, 3 z 4, itd., przepisując do drugiej tablicy, potem dwuelementowe odcinki scalaj w 4 elementowe, przepisując z powrotem do 1-szej tablicy itd. aż do odcinka posortowanego o długości tablicy, - Wersja rekurencyjna – podziel na 2 części i zastosuj metodę Merqesort, wyniki scal; gorsza od podstawowej, - Wersja naturalna; zaczyna się od serii naturalnych; kłopot z wyznaczeniem granic serii, jest atrakcyjna, gdy tablica jest częściowo posortowana. Sortowanie struktur zewnętrznych: - Trzeba mieć 3 (najlepiej 4) pliki, - Plik należy podzielić na 2 pliki (z 1 czytamy 1 podciąg i z 2, 1 podciąg wkładając do 3 pliku) potem 3 dzieli się znowu na dwa, - Dokładając 4 plik pozbywamy się przepisywania, Sortowanie pozycyjne: - Metoda liczników częstości; Zakładamy, że m=2b (b – długość słowa binarnego). Dla każdego j = 0, 1, …, m-1 liczymy, ile razy j pojawia się w ciągu wejściowym a1, …, an. Na podstawie obliczonych liczników częstości umieszczamy każdy element a i we właściwym miejscu w ciągu wynikowym, Rozmieszczenie Algorytmiczne - Wyszukanie; linowe, binarne, tablice zwane indeksami lub skorowidzami, za mało efektywne (czasowo i pamięciowo). - Tablica indeksów określająca pewien algorytm = tablica rozproszona, elementów rozmieszczenie w takiej strukturze = rozmieszczenie algorytmiczne, złożoność takiego wyszukania O (1). Tablica rozproszona: - nazywamy system T= < K,D,h > gdzie: K – zbiór n kluczy, D – zbiór p adresów, h:K→D funkcja odwzorowująca K w D (laszująca, mieszająca, kodująca, transformująca klucz w adres. - p ≥ n ; współczynnik wypełnienia ≥ n/p ( 5-10 %, n=104, 105)k Rozmieszczenie Algorytmiczne: - składa się z algorytmów: numeryzacji klucza, rozmieszczenia pierwotnego (funkcja mieszająca) i rozwiązywania konfliktów. - problemy: wybór funkcji numerującej i mieszającej, wybór metod rozwiązywania konfliktów. - zastosowania; bazy danych, budowa kompilatorów, rozwiązywanie zadań numerycznych zawierających macierze rzadkie, systemy plików. Numeryzacji Klucza. - algorytmy rozmieszczenia pierwotnego i usuwania kolizji są często numeryczne, klucz natomiast jest często alfabetyczny i na tyle długi, że jego binarnej reprezentacji nie da się interpretować wzajemni; trzeba go przetworzyć na liczbę. Metody składania: - Składanie cykliczne z sumowaniem arytmetycznym; * potnij konwertowany ciąg bitów na odcinki o długości równej długości żądanego wyniku, podpisz i sumuj arytmetycznie odrzucając ewentualny nadmiar. - Składanie cykliczne z różnicą symetryczną; * jak wyżej, ale zamiast sumy arytmetycznej zastosuj różnicą symetryczną (XOR), trochę szybsze niż poprzednie. * Składanie zygzakowe; * po podpisaniu odcinków jeden po drugim, odwróć kolejność bitów, w co drugim z nich, sumuj lub stosuj XOR, Algorytmy rozmieszczania pierwotnego. - Algorytmy rozmieszczania niezależne od rozkładu kluczy: * Randomizacja, * Metody składania, * Metoda środka kwadratu, * Metoda mnożenia, * Metoda dzielenia. - Algorytmy rozmieszczania zależne od rozkładu kluczy: * Zbieramy dane statyczne o znakach występujących w reprezentacji kluczy ze zbioru, K. Randomizacja: d (i) = rand ( k (i)) Metoda składania: - Warunek:, jeżeli przez składanie klucz został skrócony do k bitów, to długość tablicy = 2K działek, a numeryzacja działek Od 0 do 2K-1, - Własności rozpraszające: dobre, - Wady: narzucona wielkość tablicy, Metoda środka kwadratu: - Algorytm: weź klucz znumeryzowany, podnieś go do kwadratu, wybierz ze środka tyle bitów ile ci potrzeba, zinterpretuj to jako adres, - Znaczenie metody: raczej historyczne, - Zalety: względna prostota algorytmu, dobre rozpraszanie, - Wady: narzucona wielkość tablicy jw., indeksacja od 0 do 2K-1, Metoda mnożenia: NWP a hK P * * K mod 1 b (a,b)=1 a b c 5 1 0,618033... 2 - z ułamka odcina część ułamkową - Algorytm: weź ponumerowany klucz pomnóż przez rzeczywisty współczynnik C, odrzuć część całkowitą, część ułamkową pomnóż przez długość tablicy, odrzuć część ułamkową wyniku. Wynik końcowy będzie liczbą całkowitą z przedziału od 0 do 1-p gdzie p jest długością tablicy. - Zalety: dowolna długość tablicy, dobre mieszanie przy dobrze dobranym C, zaleca się, aby C było przybliżeniem liczby niewymiernej np. stosunku złotego podziału. - Wady: narzucona numeracja od 1 do p, algorytm stosunkowo skomplikowany, dwa mnożenia przez liczbę rzeczywistą plus dwie dodatkowe operacje. Metoda dzielenia (reszty z dzielenia): - Algorytm: klucz znumeryzowany podziel przez długość tablicy, weź resztę z dzielenia. - Warunek: dobre mieszanie, jeżeli długość tablicy jest liczbą pierwszą albo pseudopierwszą (żaden czynnik pierwszy nie jest mniejszy niż 20). - Zalety: bardzo prosty algorytm, dobre mieszanie. - Wady: konieczność dociągania długości tablicy do najbliższej liczby pierwszej albo pseudopierwszej. Algorytmy usuwania konfliktów: Algorytmy rozwiązywania konfliktów bez obszaru nadmiarowego: - Metody liniowe, - Metody kwadratowe; * Wariant prymitywny, * Metoda Radkego, * Metoda Daya, - Metody sześcienne, - Metody losowe, - Metody mieszane. Metody liniowe: a (i)= (a(O)+b*i) mod p, gdzie; a (0) – adres początkowy wyznaczony przez algorytm mieszający, i – numer próby usunięcia kolizji, b- współczynnik całkowity, p – długość tablicy. Wyznacznik b: wszystko jedno, jaki a więc najprościej 1 lub -1. Wzór redukuje się zatem do; a (i) = (a(0)+i) mod p albo a (i) = (a(0) - i) mod p - Zalety: prostota algorytmu brak skoków na długie dystanse, nie narzuca długości tablicy. - Wady: skłonność do tworzenia skupień pierwszego rzędu (modyfikacja; b jako zmienna, double hashing – czyli b jest inną funkcją mieszającą). - Zalecane: uważać, aby współczynnik zapełnienia nie przekraczał ok. 80%. Metody kwadratowe: - Wariant prymitywny a (i) = (a(0)+b*i+c+i2) mod p , gdzie; b, c – współczynniki rzeczywiste. Współczynnik b: wszystko jedno, jaki więc najprościej 0, Współczynnik c: wszystko jedno, jaki byle nie 0, więc najprościej 1 lub -1. Wzór upraszcza się, więc do postaci: a (i)=(a(0)+i 2) mod p albo a (i)=(a(0)-i2) mod p Wady dyskwalifikujące: powtórne badanie działek już przebadanych, pomijanie niektórych działek, skupiska drugiego rzędu. - Metoda Radkego Warunek: długość tablicy musi być „liczbą Radkego” tj. liczbą pierwszą wyrażającą się wzorem 4*i+3 gdzie, i jest dowolną liczbą naturalną. Algorytm: a(1)=(a(0)+12) mod p a(2)=(a(0)-12) mod p a(3)=(a(0)+22) mod p a(4)=(a(0)-22) mod p itd. Uwaga: niech algorytm robi tylko p-1 kroków, bo potem powtórne badania będą musiały nastąpić., Zalety: niezawodność, Wady: wymuszenie dotyczące długości tablicy, konieczność obliczania (albo przechowywania i odszukiwania) kwadratów kolejnych liczb naturalnych, powstawanie skupień drugiego rzędu. - Metoda Daya (metoda Radkego ulepszona)Metoda opiera się na spostrzeżeniu, że ciąg liczb kwadratowych (tzn. ciąg kwadratów kolejnych liczb naturalnych) 02 12 22 32 42 itd. Czyli 0, 1, 4, ,9, 16 itd. Daje po zróżnicowaniu ciąg 1, 3, 5, 7, itd. czyli ciąg kolejnych liczb nieparzystych. Powstaje stąd pomysł, aby kolejne liczby kwadratowe obliczać nie przez podnoszenie do kwadratu ale przez tworzenie i sumowanie ciągu kolejnych liczb nieparzystych. Uwaga: metoda Daya jest modyfikacją metody Radkego. Metody sześcienne: Metody te są prawdopodobnie lepsze jeżeli chodzi o ilość skupień ale wymagają większego nakładu obliczeń. d(i)=(d(0)+i3) mod p; d(i)=(d(0)+(i2 mod p)*i) mod p Skracanie czasu obliczeń może powstać przy obliczeniu sześcianu metodą reszt sześciennych. Metoda losowa. Metody mieszane: - Algorytmy rozwiązywania konfliktów z obszarem nadmiarowym: * Z dodatkową tablicą, * Z listą synonimów (tworzymy dodatkowe pole w rekordzie, aby utworzyć listę. Operacje na tablicach rozproszonych. 1. Wprowadzenie nowego zapisu – sprowadza się do szukania. 2. Wyszukanie określonego zapisu – sprowadza się do szukania. 3. Usuwanie określonego wpisu – sprowadza się do szukania i reorganizacji. 4. Inne algorytmy przetwarzania tablicy np. reorganizacja, zmiana rozmiaru tablicy, usuwanie rekordu. Usuwać nie można, można tylko zaznaczyć, że element usunięto. Reorganizacja tablic rozproszonych. 1. Przygotowanie reorganizacji. Każdy rekord wyposażamy w dwa jednakowe pola techniczne: - bit rekordu żywego b1, - bit rekordu trwałego b2 (przed uruchomieniem reorganizacji = 0) 2. Przeglądaj sekwencyjne działki tablicy aż do końca. 3. Po napotkaniu rekordu żywego, nietrwałego uruchom algorytm rozmieszczania pierwotnego (wyznacz mowy adres rekordu), jeśli martwy lub trwały to pomijamy, postępuj następująco z nowym adresem: - If działka pusta lub zajęta przez rekord martwy, then umieść nowy rekord i oznacz bitem trwałości, (czyli go to 2), - If działka zajęta przez rekord żywy, nietrwały, then wypchnij ten rekord, umieść nowy rekord i oznacz, b2=1, a rekord wypchnięty traktuj jako nowy (go to 3), - If działka zajęta przez rekord trwały, then uruchom algorytm usuwania kolizji, przejdź do działki wskazanej przez ten algorytm i (go to 1). Ogólny algorytm wprowadzania zapisu rekordu do tablicy rozproszonej (spotykany w książkach) 0: k = numeryzuj (klucz); 1: i ← 0; 2: di ← h(k); 3: if t[di] = 0 then begin t[di] ← zapis (k); ‘koniec algorytmu’ end; Struktury Danych O Podłożu Zmiennym - Bardzo ważna grupa struktur liniowych. - Są to struktury nie posiadające adresacji !. Dostęp do poszczególnych elementów struktury jest organizowany przez wyróżnienia. - W takich strukturach nie dopuszcza się elementów pustych, (pokrycie zupełne), - Do tych struktur należą: stos, kolejka, talia niesymetryczna i symetryczna, lista jedno- i dwukierunkowa. - Dla każdej z wymienionych struktur będą przedstawione: * Wyróżnienie, * Lista operacji prostych (dynamika struktury), * Ewentualne uwagi, * Zastosowania, * Lista operacji złożonych. Definicja: Struktury – stos, kolejka, talia (symetryczna i niesymetryczna) oraz lista (jedno- i dwukierunkowa) – to struktury o: - pokryciu zupełnym, - podłożu liniowym zmiennym, - o dynamice stosu (dpowiednio); stosu, kolejki, tali, listy. STOS Wyróżnienia: wierzchołek stosu, ewentualnie dno stosu. Operacje proste (5 operacji) : - Opróżnienie stosu - Testowanie : * czy pusty * na zawartość wierzchołka, - Dołączanie nowego elementu – tylko na wierzchołku, - Kasowanie elementu – tylko z wierzchołka. Uwagi : - Struktura pracuje jednym końcem, - Określana jako struktura LIFO (Last In First Out) Zastosowania : - Tam gdzie potrzebna jest realizacja zasady LIFO np. algorytmy przeglądania grafu, obliczenie wartości wyrażenia, derekursywacja programów, - Gdy zasada ta jest obojętna, stos okazuje się najprostszą strukturą o zmiennym podłożu. Operacje złożone : - Przeglądanie stosu, KOLEJKA Wyróżnienia: początek kolejki, koniec kolejki. Operacje proste (5 operacji) : - Opróżnienie kolejki, - Testowanie : * czy puste, * na zawartość działki początkowej, - Dołączanie nowego elementu – tylko na koniec, - Kasowanie elementu – tylko na początku. Uwagi : - Struktura pracuje obydwoma krańcami niesymetrycznie, - Określana jako struktura FIFO (First In First Out) Zastosowania : - Przede wszystkim jako kolejka zadań o jednakowych priorytetach, czekających na wykonanie. Operacje złożone : - Przeglądanie kolejki, - Konkatenacja kolejki, TALIA (półka) niesymetryczna Wyróżnienia : Koniec lewy i prawy (początek i koniec), Operacje proste (6 operacji) : - Opróżnianie talii, - Testowanie: * czy pusta, * na zawartość działki początkowej, - Dołączanie nowego elementu - na początek i na koniec, - Kasowanie elementu - tylko na początku, Uwagi : - Struktura pracuje obydwoma krańcami niesymetrycznie, Zastosowania : - Jako kolejka zadań do wykonania przy założeniu, że zadania dzieli się na zadania z normalnym priorytetem (dołączane na koniec) oraz zadania z priorytetem zwieszonym (dołączane na początek). Operacje złożone : - Przeglądanie talii, - Konkatenacja talii, Talia symetryczna Wyróżnienia : koniec lewy i prawy (początek i koniec). Operacje proste (8 operacji) : - Opróżnianie talii, - Testowanie: * czy pusta, * na zawartość działki początkowej, * na zawartość działki końcowej. - Dołączanie nowego elementu - na początek i na koniec, - Kasowanie elementu - na początku i na końcu. Uwagi : - struktura pracuje obydwoma krańcami symetrycznie. Zastosowania : - Jako kolejka zadań przeznaczonych do wykonania dla dwóch wykonawców, każdy bierze do wykonania w pierwszej kolejności zadania swoje, a po wyczerpaniu wykonuje zadania w zasadzie przeznaczone dla drugiego wykonawcy. Operacje złożone : - Przeglądanie talii, - Konkatenacja talii, Lista jednokierunkowa (niesymetryczna). Wyróżnienia : - Stałe: * koniec lewy (początek), * koniec prawy (koniec) - Zmienne: wyróżnienie główne (robocze). Operacje proste (9 operacji) : - Opróżnianie listy, - Testowanie: * czy pusta, * czy wyróżnienie główne na końcu, * na zawartość działki pod wyróżnieniem głównym. - Przesunięcie wyróżnienia głównego: * na początek, * na następną działkę. - Dołączanie nowego elementu : * po (na prawo od) wyróżnieniu głównym, * na koniec (niezależnie od położenia wyróżnienia głównego). - Kasowanie elementu : pod wyróżnieniem głównym (wyróżnienie główne przesuwa się wtedy jeśli możliwe na prawo, jeśli nie, to na lewo, jeśli to był ostatni to wyróżnienie znika.) Operacje złożone: - Przeglądanie i przeszukiwanie listy, - Sortowanie listy (zostanie omówione), - Konkatenacja listy. niesymetryczną – na obu listach Lista dwukierunkowa (symetryczna). Wyróżnienia: - Stałe: * koniec lewy (początek), * koniec prawy (koniec) - Zmienne: wyróżnienie główne (robocze). Operacje proste (12 operacji) : - Opróżnianie listy, - Testowanie: * czy pusta, * czy wyróżnienie główne na końcu, * czy wyróżnienie główne na początku, * na zawartość działki pod wyróżnieniem głównym. - Przesunięcie wyróżnienia głównego: * na początek, * na koniec, * na następną działkę, * na poprzednią działkę. - Dołączanie nowego elementu : * po (na prawo od) wyróżnieniu głównym, * na koniec (niezależnie od położenia wyróżnienia głównego). - Kasowanie elementu : pod wyróżnieniem głównym (wyróżnienie główne przesuwa się wtedy jeśli możliwe na prawo, jeśli nie, to na lewo, jeśli to był ostatni to wyróżnienie znika.) Operacje złożone: jak dla listy jednokierunkowej, ale z możliwością stosowania bardziej dogodnych algorytmów. - Przeglądanie i przeszukiwanie listy, - Sortowanie listy (zostanie omówione), - Konkatenacja listy. . Uwagi ogólne dla listy jedno- i dwukierunkowej Uwagi : - Stanowi konkurencje dla tablicy, - W stosunku do tablicy jest: * Bardziej elastyczna (podłoże), * Cale nie posiada adresacji, co uniemożliwia albo przynajmniej utrudnia niektóre operacje. Zastosowania : - Tekst w edytorze, - Lista rekordów (plik listowy). Lista rekordów. W każdym rekordzie znajduje się pole kluczowe i jego zawartość może decydować o rozmieszczeniu albo stanowić podstawę odszukania rekordu. Rozmieszczanie rekordów: - chaotycznie, - monotonicznie: - Uwaga : nie można rozmieszczać algorytmicznie (jak to było w tablicy), ze względu na brak adresacji. Operacje złożone: - Dołączanie rekordu : w przypadku rozmieszczenia chaotycznego jest to operacja prosta „dołącz na koniec”. W przypadku listy posortowanej – szukaj właściwego miejsca i ulokuj rekord w tym miejscu. - Odszukiwanie rekordu : sekwencyjne (binarne nie, bo odwołuje się do adresów), - Kasowanie rekordu : operacja prosta „kasuj pod wyróżnieniem” (ewentualnie poprzedzona operację odszukiwania rekordu do skasowania), - Sortowanie (monotonizacja) listy : operacja sortowania list jest bardzo ważna (mimo braku możliwości wykorzystania wyszukiwania binarnego) ze względu na ewentualne powiązania struktur listowych z tablicowymi w przetwarzaniu. Sortowanie proste : - Proste wstawianie (nadaje się) : listę rozdziela się na dwie niezależne lity (posortowaną i nieposortowaną), przenosi się kolejne elementy z listy nieposortowanej do posortowanej, unika się problemu rozpychania. - Proste wybieranie (nadaje się) : rozdziela się na dwie niezależne lity ( posortowaną i nieposortowaną ) , przenosi się z lity drugiej do pierwszej poprzez wybieranie elementu, odpada konieczność zamiany lub rozpychania. - Metoda bąbelkowa (wykonalna) : nie jest brana pod uwagę ze względu na koszt dużej ilości krótkodystansowych przestawień. Sortowanie złożone : - Quicksort (w zasadzie nie nadaje się) : powstaje niezręczność przy wyborze elementu dzielącego, wymagana jest lista dwukierunkowa, ze względu na przesuwanie się wskaźników od lewej i prawej strony równocześnie. - Quicksort (modyfikacja nadająca się do list) : tworzy się trzy listy wyjściowe – dla mniejszych, równych i większych od elementu dzielącego, wywołuje się to sortowanie dla 1 i 3 lity (np. rekurencyjnie), następnie konkatenuje 1,2 i 3 listę. - Metoda scalania (najlepsza metoda złożona do sortowania list) : lista może być jednokierunkowa, wariant cykliczny - odszukaj pierwszą i drugą serie naturalną od początku lity, scal je i dołącz do końca sortowanej listy, kontynuuj aż okaże się , że jest tylko pierwsza seria i to długości całej listy. Ocena złożoności sortowania listy rekordów: pełna ocena możliwa tylko przy znajomości zastosowanej implementacji. Implementacja Struktur Danych Implementacja struktury A na strukturze B oznacza reprezentację struktury A przez strukturę B: - Struktura A: struktura implementowana (reprezentowana), - Struktura B: struktura implementująca (reprezentująca). Definicja: Implementacja struktury A na strukturze B to odpowiedniość: - Między stanami struktur A i B; * Każdemu dopuszczalnemu stanowi struktury A powinien odpowiadać, co najmniej jeden dopuszczalny stan struktury B, * Każdemu stanowi struktury B, który to odpowiada pewnemu stanowi struktury A, powinien odpowiadać dokładnie jeden stan struktury B, - Między operacjami na strukturach A i B; * Każdej operacji prostej O, zdefiniowanej dla A, powinna odpowiadać, co najmniej jedna operacja O’ (niekoniecznie prosta), możliwa dla B, - Odpowiedniość między stanami struktur A i B oraz odpowiedniość miedzy operacjami na tych strukturach powinny być ze sobą związane następującą zasadą; * Jeżeli stanowi S struktury A odpowiada stan S’ struktury B, a operacja O na strukturze A odpowiada operacji O’ na strukturze B, to wynikowi operacji dokonanej na strukturze A w stanie S powinien odpowiadać wynikowi operacji O’ dokonanej na strukturze B w stanie S’. Twierdzenie o rozszerzonej dynamice : - Jeżeli struktury A i B mają jednakowe działki , a dynamika struktury B stanowi rozszerzenie dynamiki struktury A o dodatkowe operacje, to strukturę A można zaimplementować na strukturze B. Twierdzenie o przechodniości implementacji : - Jeżeli strukturę A da się zaimplementować na strukturze B, a strukturę B da się zaimplementować na strukturze C, to strukturę A da się zaimplementować na strukturze ( A→ B i B→ C to A→ C ). Przykłady implementacji. Implementacja tablic: - „mała” tablica A na dużej tablicy B (działki tego samego typu, tablice numerowane, numeracja A jest podprzedziałem numeracji B – nadmiarowe działki nie mają znaczenia, ale jednemu stanowi tablicy implementowanej może odpowiadać wiele stanów tablicy implementowanej), - wiele „małych” tablic A1, A2 ….. na dużej tablicy B, - tablice jak wyżej ale działka tablicy B ma mniejszą pojemność niż działka w tablicy A (jednej działce tablicy A odpowiada ciąg sąsiadujących ze sobą działek tablicy B – ciąg powinien liczyć tyle działek, by zbiór dopuszczalnych pokryć tego ciągu nie był mniejszy niż zbiór dopuszczalnych pokryć jednej działki tablicy w pamięci), - implementacja dowolnej tablicy w pamięci operacyjnej; pamięć operacyjna jest realnie istniejącą tablicą liniową numerowaną, zwykle o niewielkich działkach (1 bajt), dowolną abstrakcyjną tablice liniową numerowaną można zaimplementować na niej podobnie jak to było przedstawiane powyżej. 1. Na mocy twierdzeń o rozszerzonej dynamice i przechodniości implementacji wynika, że stos i kolejkę można zaimplementow ać na obu taliach listach, a talię 2. Implementacje struktur liniowych o podłożu zmiennym na tablicach; - Dla stosu; * stos na tablicy, * dwa stosy na tablicy (złączone dnem lub dna na krańcach tablicy), * wiele stosów na tablicy - Dla kolejki; * kolejka na tablicy (pełzanie), * wiele kolejek na tablicy, 3. Implementacje struktur liniowych o podłożu zmiennym na łańcuchach (inaczej: na strukturach odsyłaczowych, wskaźnikach); - stos na łańcuchu jednokierunkowym, - kolejka na łańcuchu jednokierunkowym, - kolejka na pierścieniu jednokierunkowym, - lista jednokierunkowa na łańcuchu jednokierunkowym, - lista dwukierunkowa na łańcuchu dwukierunkowym, Struktury pierścieniowe - Struktury pierścieniowe wykazują duże podobieństwo do liniowych. - Podłoże pierścieniowe; * W przypadku tego podłoża każda działka ma następnik, każda działka ma działkę poprzedzającą i każda działka jest z każdej osiągalna naprzód i wstecz. - W niektórych przypadkach struktury pierścieniowe są bardziej praktyczne od liniowych, Pierścień jednokierunkowy: Definicja: struktura dynamiczna o podłożu pierścieniowym zmiennym. O pokryciu zupełnym, o dynamice pierścienia jednokierunkowego. Wyróżnienia (obydwa wyróżnienia są zmienne): - umowny początek pierścienia, - wyróżnienie główne robocze, Operacje proste (9 operacji) : - Opróżnianie struktury, - Testowanie: * czy pierścień pusty, * czy wyróżnienie robocze na początku, * na zawartość działki pod wyróżnieniem roboczym. - Przesuń wyróżnienie; * główne na początek, * główne o jedną działkę na przód, * początek na miejsce głównego, - Dołączanie elementu : * np. na prawo od wyróżnienia głównego (tylko jedno w odróżnieniu od listy jednokierunkowej), - Kasowanie elementu : pod wyróżnieniem głównym (wyróżnienie główne przesuwa się o jedną działkę na prawo), łatwiejsza implementacja kasowania ZA wyróżnieniem głównym. Pierścień dwukierunkowy Definicja: na podstawie pierścienia jednokierunkowego i listy dwukierunkowej. Struktury drzewiaste Definicja: 1) Drzewa grafowe (jest to drzewo bez korzenia albo z korzeniem definiowane jako szczególny przypadek grafu). Drzewo grafowe to struktura statyczna nie będąca strukturą danych, może być traktowana jako statyczna struktura danych, jeżeli jego węzły zostaną potraktowane jako działki podłoża struktury. Dynamiczna struktura danych to struktura, której stany są statycznymi strukturami drzewiastymi. Zbiór dopuszczalnych stanów i dynamikę takiej struktury można definiować na wiele sposobów uzyskując m.in.: - mnogościowe, - tablicowe (w tym binarne), - listowe, Zamiast drzew grafowych z dołączoną dynamiką można wykorzystywać uprzednio zdefiniowane dynamiki struktur mnogościowych i liniowych, i rekurencyjnie definiować te same drzewa, wprowadzając dynamikę równocześnie z tymi elementami definicji, które dotyczą podłoża i pokrycia. 2) Drzewa rekurencyjne (wielowarstwowe): - mnogościowe, - tablicowe (binarne, pozycyjny), - listowe, Drzewo mnogościowe (rekurencyjne) Drzewo rekurencyjne mnogościowe to rekord składający się z dwóch pól: a) pola korzenia pokrytego przez daną składową, b) pola rozgałęzienia pokrytego przez las mnogościowy. Las mnogościowy to zbiór (w sensie mnogościowym struktura danych) drzew mnogościowych. Las rozgałęziający to las pokrywający pole rozgałęzienia. Jeżeli las rozgałęziający jest pusty drzewo redukuje się do bezpotomnego korzenia. Drzewo elementarne – drzewo zredukowane do korzenia. Można tu przenieść wprowadzone dla struktur grafowych pojęcia takie jak: liść, ścieżka promieniowa, poddrzewo, poziom, relacja ojciec syn itd. Zastosowanie: reprezentacja klas równoważnościowych w zbiorach o znanym składzie (w przypadku komputerowej analizy grafu może to być – w szczególnym przypadku – ewidencja przynależności wierzchołków grafu do jego części spójnych) Drzewo rekurencyjne tablicowe Drzewo rekurencyjne tablicowe to rekord składający się z dwu pól: - pola korzenia pokrytego przez daną składową oraz, - pola rozgałęzienia pokrytego przez las tablicowy. Las tablicowy to tablica o działkach pokrytych przez drzewa tablicowe. Tablica rozgałęziająca – tablica pokrywająca pole rozgałęzienia. (Jeżeli tablica rozgałęziona jest pusta tzn. działki pozbawione pokrycia, to korzeń bezpotomny) Drzewo (tablicowe) elementarne jw. Szczególnym przypadkiem rekurencyjnego drzewa tablicowego jest drzewo binarne. Szczególne przypadki rekurencyjnego drzewa binarnego: drzewo binarne pełne, i drzewo binarne kompletne. Drzewo rekurencyjne binarne. Drzewo tablicowe, w którym wszystkie tablice rozgałęziające są tablicami o dwóch działkach (działka: pierwsza, druga; bądź działka: lewa, prawa). Numeracja drzewa binarnego pełnego; Numeryzacja poziomami Drzewo kompletne (uwagi); - Drzewo kompletne może może mieć dowolną liczbę węzłów, - Jeżeli n=2k-1, gdzie k=1, 2, 3, … to drzewo kompletne staje się drzewem pełnym. - Jeżeli węzły drzewa kompletnego ponumerujemy poziomami jak wyżej, to pierwszy syn węzła k ma numer 2k, a drugi 2k+1, zaś ojciec węzła o numerze k ma numer kdiv2, węzły numer 1 do ndiv2 są ojcami (mają co najmniej 1 syna), pozostałe są liściami. Implementacje (tekstowe, odsyłaczowi, poziomami) drzew pełnych albo kompletnych to implementacje tych drzew na tablicy liniowej numerowanej, (obrazem węzła o numerze k jest działka tablicy mająca także numer k). Uwaga:, ponieważ tablica kompletna może mieć dowolną liczbę węzłów, to każdą tablicę liniową 1…n można uznać za tablicę implementującą pewne kompletne drzewo binarne. Odsyłaczowe i tekstowe implementacje drzew binarnych Dla drzew binarnych niebędących drzewami kompletnymi implementacja poziomami uciążliwa i mało wydajna. Stąd implementacje odsyłaczowe, czasem tekstowe. Implementacja trójodsyłaczowa: Każdy rekord wyposażony jest w trzy odsyłacze: Implementacja dwuodsyłaczowa: - oszczędniejsza pamięciowo - wymaga przeglądnięcia od korzenia (notatnik stosowy, gdy potrzebny powrót do korzenia) Rekurencyjne implementacje tekstowe 1. + a,b korzeń – lewe – prawe KLP prefiksowa (przedrostkowa) – Łukasiewicza (Polska) 2. (a + b) – (konieczne nawiasy) lewe – korzeń – prawe LKP infiksowi (wzrostkowa) 3. a,b + lewe – prawe – korzeń LPK postfiksowa (przyrostkowa) – Odwrotna Notacja Polska Analogia do gramatyki, odpowiedniki w konwencjach zapisu wyrażeń arytmetycznych i logicznych. Ulepszona metoda wybierania Sortowanie stogowe (poprzez kopcowanie) (stosujemy kolejki priorytetowe) Stóg definiujemy jako ciąg kluczy h2, h2+1, … hp takich że hi <= h2i , hi <= h2i+1 dla wszystkich l = p/2 . Kolejne zdjęte wartości max z wierzchołka stogu dają posortowany ciąg w odwrotnej kolejności. (mniejsza liczba porównań w stosie do prostej metody wybierania przez zapamiętania większej ilości informacji – drzewo binarne. budowa stogu = O(n) n – usunąć min*O(log n) operacja usunięcia = O(nlog n). czy można bez jeszcze dodatkowej pamięci ? – tak 1. działa w miejscu 2. dobra dla dużych (wtedy lepsze również od Shella) 3. dobra efektywność nawet w najgorszym przypadku 0(n log n) 4. faworyzuje częściowe uporządkowanie w kolejności odwrotnej - zachowanie nienaturalne porównanie: N(n) = 2n logn + O(n); A(i) => nie znana pełna analiza O(n logn) współ. ~ 2 (lepszy będzie QuickSort) Kopiec i sortowanie kopcowe Porządek kopcowy: (≠ porządku symetrycznego dla BST) Odsyłaczowe i tekstowe implementacje drzew binarnych CD. Dla drzew binarnych nie będących drzewami kompletnymi implementacja pomiędzy poziomami => uciążliwa i mało wydajna stąd implementacje odsyłaczowi, czasami tekstowe. {sortowanie stogowe} procedure heapsort (var a:input_data; n:integer); var a:input_type; procedure downheap (k:integer); var i,j : integer; v : input_type ; s:boolean; begin v:=a[k]; while k<=n div 2 and not s do begin j := 2*k; {j jest nastepnikiem k} if j<n then if a[j]<a[j+1] then j := j+1; if v>=a[j] then begin goto label1; s:=true; end else begin a[k]:=a[j]; k:=j; end; label1: a[k] := v; end; deletmax {function datemax : integer;} procedure datemax (var d:input_type); begin d:=a[1]; a[1]:=a[n]; n:=n - 1; downheap(1); end; procedure construct; var i : integer; begin for i:=n div 2 downto 1 do downheap(i); end; begin construct; repeat deletemax(x); a[n] := x; until n=1; end. {dla input_type = integer lepsza function datemax;}a[n] = datemax; Przeglądanie drzewa: - prefiksowe (przedrostkowe, wzdłużne) – KLP - infiksowe (wzrostkowe, poprzeczne) – LKP - postfiksowe (przyrostkowe, wsteczne) – LKP type link=^node node=rekord key=T left,right=link; end; procedure KLP (t:link); ← przez wartość begin if t<>nil then begin P(t);←procedura odwiedzania wierzchołka KLP (t^.left); KLP(t^.right); end; end; Konstrukcja drzewa dokładnie wyrażonego 1. użyj jeden węzeł na korzeń 2. zbuduj lewe poddrzewo z nl = n div 2 węzłami w ten sposób 3. zbuduj prawe poddrzewo z np. = n – nl – 1 węzłami w ten sposób type link = ^node; node = record el:El; left,right:link; end; function tree ( n:integer) : link; var p:link; x:T; begin if n=0 then tree:=nil else begin read(x); new(p); ……………….. p^.el:= x; p^.left:= tree( n div 2); p^.right:= tree( n – n div 2 – 1 ); tree := p; end end { drzewo – tree} begin root:=tree(100); end. Plik drzewiasty binarny - drzewo binarne o działkach pokrytych przez rekordy jednakowego typu. Binarne drzewo poszukiwań – Binary Serach Tree BST Jest to plik drzewiasty binarny tworzony wg następującego algorytmu: 1. pierwszy rekord wstawiany do pliku umieść w korzeniu. 2. każdy następny rekord wprowadzaj przez korzeń porównując klucze : rekord o kluczu większym do lewego poddrzewa, o kluczu większym do prawego poddrzewa, powtarzaj w korzeniu każdego kolejnego poddrzewa. Jeśli dojdziesz do poddrzewa pustego – rekord umieść jako liść. (porządek symetryczny) x: left(x), key(x), right(x) Wg(1) tworzy się drzewo. Wg zasady tworzenia (1,2) prowadzi się również wyszukiwanie. Operacja wyszukiwania jest częścią składową operacji wstawiania i usuwania wierzchołków. Usuwanie wierzchołka można przeprowadzić zastępując go wierzchołkiem zawierającym albo element bezpośrednio poprzedzający wartość elementu usuwanego w zbiorze wartości, (b) albo element bezpośrednio następujący. Opóźnione usuwanie ( zaznaczamy, że usunięty => potem reorganizacja). BST type tree_ptr = ^tree_node; tree_node = record element : element_type; left,right : tree_ptr; end; SEARCH_TREE = tree_ptr; {tworzenia drzewa pustego} procedure make_null ( var T:serach_tree); begin t:=nil; end; {operacja szukania} function find ( x : integer; T:search_tree) : tree_ptr; begin if T = nil then find:=nil else if x < T^.element then find:=find (x,T^.right) else find:=T; end; {find} function find_min ( T:search_tree):tree_ptr; begin if T = nil then find_min := nil else if T^.left = nil then find_min:=T else find_min := find_min ( T^.left); end; function find_max (T:…..):tree_ptr; begin if T<>nil then while T^.right <> nil do T := T^.right; find_max := T; end; procedure insert ( x:element_type; var T:…..); begin if T = nil then begin {tworzymy i zwracamy 1 węzeł, korzeń} New(T); if T = nil then fata_error(‘out of space’) else begin T^.element := x; T^.left := nil; T^.right := nil; end; end {if T = nil} else begin if x < T^.element then insert ( x, t^.left) { else x jest w drzewie, nic nie robimy} end; end: {insert) Drzewo poszukiwań BST - usuwanie wierzchołka – wersja rekurencyjna - 3 możliwości: 1. brak węzła w danym kluczu 2. węzeł o kluczu x ma 0 lub 1 potomka 3. węzeł o kluczu x ma 2 potomków Węzeł usuwany musi być zastąpiony przez skrajny prawy element lewego poddrzewa lub lub skrajny lewy węzeł prawego poddrzewa z których każdy ma co najwyżej jednego potomka. Procedure delete ( v:T; var p:link); Var q : link; procedure del (var v:link); begin if r^.right <> nil the del(r^.right) else begin q^.key := r^.key; {ewentualne inne pola rekordu} q := r; r:= r^.left; end; end begin if p = nil then writeln(‘Brak’) else if v < p^.key then delete(v, q^.left) else if v>p^.key then delete(v,p^.right) else begin {delete p^} q:=p; if q^.right = nil then p:=q^.left else if q^.left = nil then p:=q^.right else del(q^.left); dispose(q); end; end. var y:node {poprzednik wierzchołka szukanego} function search (v, p, r :node):node { T- dowolny typ uporządkowany liniowo r – korzeń drzewa BST} var x:node; Begin x := r; y := nil; while (x ≠ nil) and (key(x) ≠ v) do begin y := x; if v < key(x) then x := left(x) else x := right(x); end; search := x; end. {search}{ jeżeli element należy do BST → x ≠ nil i key(x) = v, jeżeli element nie należy do BST → x = nil.} procedure insert (v:T; var r:node); var x,y:node Begin if search(v, r, y) = nil then begin new(x); left(x) := nil; key(x) := v; right := nil; if y = nil then r := x; {r – korzeń} else if v < key(y) then left(y) := x; else right(y) := x; end; end. {insert} procedure construct (var r:node); begin r :=nil: end. {construct} procedure delete (v:T; var r:node); var x, y, z, t:node; b: 0…1; Begin x := search (v, r, y); if x ≠ nil then {jeśli znaleźliśmy usuwamy} begin if (left(x) = nil) or (right(x) = nil) then begin if (left(x) = nil) and (right(x) = nil) then z := nil else if left(x) = nil then x := right(x) else z := left(x); if y = nil then r := z; {za korzeń} else if x = left(x) ten left(y) := z else right(z) := z end; else begin {left(x) ≠ nil; right(x) ≠ nil} b := random(z); { ??? random(2)???} Drzewa AVL Drzewo BST jest drzewem AVL wtedy, kiedy dla każdego wierzchołka wysokości dwóch jego bliźniaczych poddrzew różnią się co najwyżej o 1. Wysokość drzewa Al 0(logn) {n- liczba wierzchołków}→ czas działania na drzewie ≠ O(logn)Operacje (działania na drzewie);serach – taka sama jak dla drzewa BST Struktury Grafowe Wstęp – Definicje 1. Intuicyjna definicja grafu (kropki, odcinki strzałki) 2. Klasyfikacja grafów: - grafy proste i multigrafy; - grafy skierowane i niekierowane; 3. Definicja grafu prostego nieskierowanego; G = (V,E); (Vertices, Edges); V – zbiór wierzchołków, E – zbiór krawędzi, Krawędź to nieuporządkowana para wierzchołków (różnych), E { (v1,v2) │v1, v2 V v1 ≠ v2 } (Zakaz krawędzi równoległych zapętlonych) Klasyfikacja Grafowych Struktur Danych 1. Według rodzaju grafu; Grafowa struktura danych może być oparta na; - grafie prostym niekierowanym, - grafie prostym skierowanym, - multigrafie niekierowanym, - multigrafie skierowanym, 2. Według sposobu wykorzystania grafu; Jako działki podłoża mogą być wykorzystane; - wierzchołki grafu – struktura wierzchołkowa, (krawędzie = powiązanie między działkami), - krawędzie grafu – struktura krawędziowa (np. w grafie ważonym), - wierzchołki i krawędzie grafu – struktura wierzchołkowo - krawędziowa, (rolę powiązań spełniają elementy relacji incydencji), 3. Według regularności grafu; Podłoże struktury grafowej może być; - regularne (np. w przypadku tablic wielowymiarowych), macierze zupełne; * pasmowe, * trójkątne, * symetryczne, * antysymetryczne macierze rzadkie, - nieregularne, Graf etykietowany: Wierzchołki = działki, Krawędzie = elementy powiązania działki, Wierzchołkom przypisujemy etykiety = dane składowe struktury danych, Graf ważony: Elementy przypisane do krawędzi → wagi, Krawędzie = działki, Dynamika Struktur Grafowych Operacje proste: - opróżnienie struktury, - wyróżnienie poszczególnych działek (np. wierzchołków), - operacje na wyróżnieniach; * przenoszenie wyróżnienia; ^ z wierzchołka na wierzchołek, ^ z krawędzi na wierzchołek, ^ z wierzchołka na krawędź, - operacje na pokryciu; * pokrywanie wskazanego wierzchołka lub krawędzi, * zdejmowanie pokrycia, * zmiana pokrycia, - operacje zmieniające podłoże; * dołączanie wierzchołka (separowanego) – dołączanie krawędzi, * odłączanie wierzchołka (separowanego) – odłączanie krawędzi, * odłączanie podgrafu, * dołączanie podgrafu, 4. Postępujemy tak dopóki stos nie jest pusty. UWAGA: Nie przechodzimy przez wszystkie krawędzie. Przejrzane wierzchołki i krawędzie to „maksymalny” podgrafu acykliczny. Przykład: Implementacja Grafu Prostego Nieskierowanego Dwa rodzaje implementacji: A - odsyłaczowe, B – tablicowe. A: - Dla grafów etykietowanych i ważonych, 2 listy: węzłów i krawędzi, Przeglądanie Stosowe: Analogicznie jak dla stosowego tylko dynamika stosowa zastąpiona dynamiką kolejki; nowe rzeczy wrzucamy na koniec, stare pobieramy z początku. 1. Zaczynamy od dowolnego wierzchołka; odnotowujemy go w kolejce i markujemy, 2. Według kolejkowego notatnika pobieramy następny wierzchołek nie zamarkowany, 3. Jeśli początkowy wierzchołek w kolejce nie ma już nie zamarkowanych następników zdejmujemy go z kolejki i powtarzamy punkt 2. 4. Dopóki kolejka nie jest pusta kontynuujemy. UWAGA: Drzewo rozpinające jest korzystniejsze, bardziej rozgałęzione. Przykład: 5, 13, 12, 10, 9, 3, 1, 2, 6, 7, 11 5, 13, 12, 10, 9, 3, 1, 2, 6, 5, 13, 12, 10, 9, 3, 4 5, 13, 12, 10, 9 5, 13, 12, 8 → pusty stos koniec ! Lista Krawędzi X – dla grafów etykietowanych (nie ważonych) 5, 13, 9, 4, 2, 7 13, 9, 4, 2, 7, 12, 11 9, 4, 2, 7, 12, 11, 10, 3 2, 7, 12, 11, 10, 3, 6, 1 12, 11, 10, 3, 6, 1 - lista wierzchołków adiacentnych – linia czerwona, - wskazuje na najbliższego sąsiada – linia niebieska, LISTA SĄSIADÓW (listy sąsiedztwa) → (macierz sąsiedztwa), B: - Tablicowa incydentna, Wierzchołki ↔ krawędzie ↓ e1 e2 E3 e4 e5 1 1 1 0 0 0 2 1 0 0 0 0 3 0 0 1 0 1 4 0 0 1 1 0 5 0 1 0 1 1 W każdej kolumnie mogą być tylko dwie jedynki !!! B: - Tablicowa adiacentna, przekątna zawsze pusta 1 2 3 4 5 1 0 1 0 0 1 2 1 0 0 0 0 3 0 0 0 1 1 4 0 0 1 0 1 5 1 0 1 1 0 Tablica kwadratowa symetryczna Dynamika Struktur Grafowych CD: Operacje złożone: Najczęściej jest to; - przeglądanie grafu, - budowa drzewa rozpinającego, - badanie spójności grafu, - badanie cykliczności grafu, - badanie planarności grafu, - poszukiwanie maksymalnego podgrafu pełnego (kliki), - i inne, Zakładając graf etykietowany, nie ważony; - przeglądanie stosowe (wzdłuż) – metoda w głąb z użyciem notatnika stosowego, - przeglądanie kolejkowe (wszerz) – metoda w szerz z użyciem notatnika kolejkowego, (Notatnik służy w obu przypadkach służy do informowania o kolejności przeglądania) Przeglądanie Stosowe: 1. Zaczynamy od dowolnego wierzchołka; odnotowujemy go na stosie i markujemy, 2. Według stosowego notatnika idziemy do wierzchołka następnego nie zamarkowanego, 3. W ten sposób postępujemy tak długo, aż dany wierzchołek nie ma nie zamarkowanych sąsiadów. Wtedy zdejmujemy go ze stosu i wracamy do punktu 2. Wada: uciążliwość przeszukiwań. Implementacja odsyłaczowa: ↓ koniec kolejki ! Graf Prosty Skierowany Jest to para G = (V, E) złożony z dwóch zbiorów; V - zbiór wierzchołków (dowolny), E – zbiór krawędzi (zbiór uporządkowanych par wierzchołków) Zabronione krawędzie zapętlone. Uporządkowanie znaczymy strzałką. Graf nie skierowany możemy uważać jako szczególny przypadek grafu skierowanego (krawędzi nie ma lub są (v1, v2) i (v2, v1). Operacja neutralizacji grafu; - krawędzie równoległe nie ruszamy, - pojedyncze krawędzie uzupełniamy dodatkową krawędzią w drugą stronę, - uzupełniamy wszystkie krawędzie, Incydencja; - dodatnia incydencja, - ujemna incydencja, - incydencja (krawędzie w obu kierunkach), Implementacje grafów skierowanych (tablicowe i odsyłaczowe); 1. Tablica incydencji: +1, -1, =1, 2. Tablica adiacencji: nie musi być już symetryczna, 3. Lista krawędzi: 1 pole = początek, 2 pole = koniec krawędzi, 4. Lista sąsiadów: lista odsyłaczy do sąsiadów – lewo i prawo stronnych = 2 listy do jednego węzła lub pole bitowe z jedną listą. Multigrafy Mogą być skierowane i nie skierowane (multigrafy nie skierowany można oczywiście też doprowadzić do skierowanego → dalej o skierowanych) Definicja: Ciąg trzech elementów G = (V, E, ŋ) V- dowolny zbiór (jak w grafie prostym) E – dowolny zbiór (odmiennie niż w grafie prostym!) jedyny warunek: rozłączny w stosunku do V (krawędzie definiowane są samoistnie, nie może być czegoś co jest węzłem i krawędzią) ŋ - funkcja incydencji – przypisuje każdej krawędzi parę wierzchołków, 1początek, 2-koniec. ŋ :E→VxV ŋ – nie musi być różnowartościowa (dopuszczalne są krawędzie równoległe i zapętlone) Implementacja struktur danych opartych na multigrafach: 1. Odsyłaczowa (lista krawędzi incydentnych) – jak grafy proste (lista wierzchołków + lista krawędzi + połączenia incydentna), 2. Tablicowa (incydencja dla obu typów naraz) – tablica incydencji nie 3 a 4 możliwości (krawędź zapętlona (podwójna incydencja)). Zastosowania: do modelowania sieci, do obwodów elektrycznych, sieci hydraulicznych i komunikacyjnych, komunikacyjnych także do diagramów ORD w podejściu obiektowym. Struktury danych: - ważone, - etykietowane, - z obydwoma rodzajami połączeń (szeroko stosowane). Struktury regularne (linearyzacja) 1. Przykład: tablice trójwymiarowe, 2. Linearyzacja leksykograficzna (najszybciej zmienia się indeks ostatni) Np. A(1,1,1); A(1, 1, 2); A(1, 2, 1); A(1, 2, 2); …A(4, 3, 2) 3. Linearyzacja antyleksykograficzna (najszybciej zmienia się indeks pierwszy) Np. A(1,1,1); A(2, 1, 1); … A(4, 1, 1); A(1, 2, 1); …A(4, 3, 2) Macierze niepełne Macierz = tablica dwuwymiarowa Linearyzacja leksykograficzna = linearyzacja wierszowa Linearyzacja antyleksykograficzna = linearyzacja Kolumnowa Macierz niepełna to macierz o dużej ilości elementów trywialnych (np. działki puste, zerowe, spacje, powtarzające się pokrycie). Macierz pasmowa: elementy nietrywialnie na przekątnej głównej, w pasie symetrycznie rozmieszczonym względem przekątnej. Macierze trójkątne: Implementacja: część nietrywialna poniżej linii podziału mieści się w części w części pustej powyżej linii podziału. Macierze symetryczne: nietrywialna część macierzy symetrycznej jest macierzą trójkątną dużą. Macierze antysymetryczne: nietrywialna część macierzy antysymetrycznej jest macierzą trójkątną małą. Macierze rzadkie Są to macierze o niewielkiej ilości elementów trywialnych (kilka % np. macierze adiacencji i incydencji implementujące strukturę grafu). Implementacja na triadach: Triada – rekord o trzech polach i – numer wiersza, j – numer kolumny, A (i,j) – pokrycie działki Struktura implementująca – lista o działkach pokrytych przez triady Zaleta: prostota struktury. Problemy grafowe - Problem najkrótszej drogi, - Problem minimalnego drzewa rozpinającego, - Problem maksymalnego przepływu, - Problem najtańszego przepływu, - Najliczniejsze skojarzenie, - Problem ścieżki Hamiltona (TSP), - Problem kolorowania grafu, - Wyznaczenie składowych spójnych, - Wyszukiwanie cykli, - Wyszukiwanie klik, - Problem planarności, - Problem wizualizacji grafu. Sortowanie cd Sortowania zewnętrzne: - przez łączenie proste (sortowanie wyważone i nie wyważone), - przez łączenie naturalne (sortowanie wyważone i nie wyważone), - przez wielokierunkowe łączenie wyważone, - sortowanie polifazowe, - sortowania mieszane. Sortowania o złożoności liniowej: - licznikowe (countsort, radixsort), - rozrzutowe (kubełkowe). Sortowania wielo kluczowe (pozycyjne): - na wielu równorzędnych kluczach, - na zhierarchizowanych kluczach częściowych. Sortowania zewnętrzne przez łączenie proste (jednofazowe i dwufazowe) - wykorzystujemy łączenie jak w klasycznym mergsort, - występują dwie fazy; * faza dzielenia – rozdzielania danych na dwa pliki (taśmy), * łączenie dwóch plików (taśm) w trzeci, - powstałe sortownie dwufazowe posiada bezproduktywne kopiowanie elementów, - poprawa: sortowanie jednofazowe wyważone. Sortowania zewnętrzne przez łączenie naturalne - wykorzystujemy serie naturalne, - jak w poprzednim przypadku mamy; * sortowanie nie wyważone (łączymy pierwszą serią pierwszego pliku, z pierwszą serią drugiego pliku, aż do skończenia jednego pliku, pozostałą resztę pliku kopiujemy), * sortowanie wyważone (dodajemy czwarty plik (taśmę) i fazę łączenia zastępujemy fazą łączenia z jednoczesnym rozdzielaniem serii na dwie taśmy), - uwaga na łączenie się serii. Wielokierunkowe łączenie wyważone - praca sortownia wewnętrznego jest proporcjonalna do liczby potrzebnych przebiegów (bo w całym przebiegu kopiuje się cały zbiór danych), - stąd rozwiązaniem może być rozkładanie danych na więcej niż 2 pliki (taśmy), * scalamy z kilku plików, nie wolno kończyć scalania pików, gdy któryś się skończy, należy kontynuować jeszcze dla aktywnych * trzeba zamieniać pliki wejściowe z wyjściowymi (tablica plików), * ilość plików (N) wchodzi do podstawy logarytmu przy rzędzie złożoności algorytmu (Xn logN n,) - uwaga na łączenie się serii. Sortowanie polifazowe - dla trzech plików (taśm); * skoro tylko jeden z plików źródłowych się skończy, staje się natychmiast taśmą wynikową dla operacji łączenia z jeszcze nie wyczerpanej taśmy źródłową oraz taśmy, która uprzedni była taśma wynikową, - dla większej ilości taśm; Metody mieszane Wykorzystuje się tablice i pliki; tablice wykorzystuje się do tworzenia serii początkowych. - Metoda buforowa – serie równe długości bufora, - Metoda przelotowa – serie nierówne, o długości większej na ogół od bufora. Sortowania licznikowe - Sortowanie licznikowe mountsort, - Sortowanie licznikowe radixsort. Sortowania na wielu kluczach równorzędnych - Wykorzystujemy sortowania pośrednie, - Np. dla sortownia skorowidzowego wprowadzamy tyle skorowidzów ile jest interesujących nas pól kluczowych i przeprowadzamy sortowania logiczne tych skorowidzów, - Uwaga; etap fizyczny sortowania może być przeprowadzony tylko według jednego z kluczy, pozostałe skorowidze ulegają dezaktualizacji.