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

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

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

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

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

Listy, kolejki, stosy

ALGORYTMY I STRUKTURY DANYCH

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

Teoretyczne podstawy informatyki

Struktury danych: stos, kolejka, lista, drzewo

ALGORYTMY I STRUKTURY DANYCH

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

Abstrakcyjne struktury danych - stos, lista, drzewo

INFORMATYKA. Podstawy programowania w języku C. (Wykład) Copyright (C) 2005 by Sergiusz Sienkowski IME Zielona Góra

Algorytmy i Struktury Danych

Dynamiczne struktury danych

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

Algorytmy i struktury danych. wykład 5

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:

Algorytmy i Struktury Danych

Stos LIFO Last In First Out

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

Teoretyczne podstawy informatyki

Drzewa binarne. Drzewo binarne to dowolny obiekt powstały zgodnie z regułami: jest drzewem binarnym Jeśli T 0. jest drzewem binarnym Np.

Drzewa poszukiwań binarnych

Ogólne wiadomości o grafach

Programowanie obiektowe

Wstęp do programowania

Porządek symetryczny: right(x)

Podstawy informatyki 2

Podstawy informatyki 2. Podstawy informatyki 2. Wykład nr 2 ( ) Plan wykładu nr 2. Politechnika Białostocka. - Wydział Elektryczny

Podstawy Informatyki. Wykład 6. Struktury danych

liniowa - elementy następują jeden za drugim. Graficznie możemy przedstawić to tak:

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

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

Lista liniowa dwukierunkowa

Algorytmy i. Wykład 3: Stosy, kolejki i listy. Dr inż. Paweł Kasprowski. FIFO First In First Out (kolejka) LIFO Last In First Out (stos)

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

Algorytmy i Struktury Danych.

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

Programowanie obiektowe

Każdy węzeł w drzewie posiada 3 pola: klucz, adres prawego potomka i adres lewego potomka. Pola zawierające adresy mogą być puste.

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

0-0000, , , itd

Podstawowe struktury danych

Algorytmy i Struktury Danych

Dynamiczne struktury danych

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

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

Wykład X. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2016 Janusz Słupik

Wstęp do programowania

INFORMATYKA DANE.

Grafy (3): drzewa. Wykłady z matematyki dyskretnej dla informatyków i teleinformatyków. UTP Bydgoszcz

Tadeusz Pankowski

Zofia Kruczkiewicz, Algorytmu i struktury danych, Wykład 14, 1

STRUKTURY DANYCH I ZŁOŻONOŚĆ OBLICZENIOWA STRUKTURY DANYCH I ZŁOŻONOŚĆ OBLICZENIOWA. Część 3. Drzewa Przeszukiwanie drzew

Podstawowe pojęcia dotyczące drzew Podstawowe pojęcia dotyczące grafów Przykłady drzew i grafów

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

Drzewa wyszukiwań binarnych (BST)

WYKŁAD 10. Zmienne o złożonej budowie Statyczne i dynamiczne struktury danych: lista, kolejka, stos, drzewo. Programy: c5_1.c, c5_2, c5_3, c5_4, c5_5

Programowanie obiektowe

ALGORYTMY I STRUKTURY DANYCH

Matematyka dyskretna - 7.Drzewa

WSTĘP DO INFORMATYKI. Drzewa i struktury drzewiaste

Przykładowe B+ drzewo

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

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

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

Struktury Danych i Złożoność Obliczeniowa

. Podstawy Programowania 2. Drzewa bst - część pierwsza. Arkadiusz Chrobot. 22 maja 2016

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

Algorytmy równoległe: ocena efektywności prostych algorytmów dla systemów wielokomputerowych

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

Struktury dynamiczne

Ogólne wiadomości o drzewach

Matematyczne Podstawy Informatyki


Obliczenia na stosie. Wykład 9. Obliczenia na stosie. J. Cichoń, P. Kobylański Wstęp do Informatyki i Programowania 266 / 303

7a. Teoria drzew - kodowanie i dekodowanie

Struktury. Przykład W8_1

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Algorytmy równoległe. Rafał Walkowiak Politechnika Poznańska Studia inżynierskie Informatyka 2010

Algorytmy równoległe: ocena efektywności prostych algorytmów dla systemów wielokomputerowych

Drzewa poszukiwań binarnych

E S - uniwersum struktury stosu

< K (2) = ( Adams, John ), P (2) = adres bloku 2 > < K (1) = ( Aaron, Ed ), P (1) = adres bloku 1 >

Podstawy Informatyki. Metody dostępu do danych

Wykład 8. Drzewo rozpinające (minimum spanning tree)

1. Algorytmy przeszukiwania. Przeszukiwanie wszerz i w głąb.

PODSTAWY INFORMATYKI wykład 6.

BAZY DANYCH. Microsoft Access. Adrian Horzyk OPTYMALIZACJA BAZY DANYCH I TWORZENIE INDEKSÓW. Akademia Górniczo-Hutnicza

Wstęp do programowania. Drzewa. Piotr Chrząstowski-Wachtel

Dynamiczny przydział pamięci (język C) Dynamiczne struktury danych. Sortowanie. Klasyfikacja algorytmów sortowania. Algorytmy sortowania

Algorytmy i struktury danych

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Lista, Stos, Kolejka, Tablica Asocjacyjna

Przypomnij sobie krótki wstęp do teorii grafów przedstawiony na początku semestru.

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

Wstęp do programowania. Listy. Piotr Chrząstowski-Wachtel

Materiał uzupełniający do ćwiczen z przedmiotu: Programowanie w C ++ - ćwiczenia na wskaźnikach

Transkrypt:

dr inż. Paweł Myszkowski Politechnika Białostocka Wydział Elektryczny Elektronika i Telekomunikacja, semestr II, studia stacjonarne I stopnia Rok akademicki 2015/2016 Wykład nr 11 (11.05.2016)

Plan prezentacji: dynamiczne struktury danych i ich obsługa lista lista jednokierunkowa lista dwukierunkowa lista cykliczna drzewo drzewo binarne

Lista (list) listą nazywamy liniową sekwencję elementów powiązanych ze sobą tak, że każdy element "zna" co najmniej jednego ze swoich sąsiadów nowy element można dodać w dowolnym miejscu listy (na początku, w środku lub na końcu) z listy możemy usunąć także dowolny jej element zasady zarządzania: stos LIFO (Last In First Out) kolejka - FIFO (First In First Out) lista brak zasad

Lista jednokierunkowa każdy element "zna" tylko jednego ze swoich sąsiadów ostatni element nie "zna" żadnego sąsiada istnieje konieczność "pamiętania" pierwszego elementu (head) lista jednokierunkowa różni się od kolejki tylko obsługą można stwierdzić, że kolejka to szczególny przypadek listy jednokierunkowej kolejka - FIFO (First In First Out) lista jednokierunkowa brak zasad

Lista (list) Graficzna prezentacja listy jednokierunkowej in out NULL NULL NULL HEAD

Implementacja listy jednokierunkowej w języku C struct lista int ; struct lista *wsk; ; //definicja pojedynczej struktury // mogą być innego typu, może być też więcej pól //wskaźnik wskazujący na następny element struct lista *head; //deklaracja elementu na początku listy ("głowa") // jest to zmienna globalna // nie ma konieczności pamiętania "ogona" // wygodnie jest jednak pamiętać "głowę", "ogon" // i element bieżący

/* funkcja dodaje nowy element za elementem podanym jako parametr */ void in (int data, struct lista *elem) struct lista *tmp; //zmienna pomocnicza elem tmp = malloc(sizeof(struct lista); tmp-> = data; if (head!= NULL) //jeśli lista nie była pusta if (elem!= NULL) //wstawiamy w środku tmp->wsk = elem->wsk; elem->wsk = tmp; tmp

else //wstawiamy na początku tmp->wsk = head; head = tmp; else //jeśli zaś lista była pusta head = tmp; //powstaje nowa "głowa" head->wsk = NULL; tmp HEAD tmp HEAD HEAD NULL

/* funkcja usuwa z listy element podany jako parametr */ void out (struct lista *elem) elem struct lista *tmp; HEAD if ((head!= NULL) && (elem!= NULL)) //jeśli lista nie była pusta //i podano prawidłowy element if (elem == head) //jeśli to "głowa" ma być usunięta tmp= head->wsk; //zmienna tymczasowa wskazuje na element za "głową" free(head); //"głowa" jest usuwana zwolnienie pamięci head = tmp; //zmienna tymczasowa staje się nową "głową" tmp HEAD

else //jeśli inny element niż głowa tmp=head; //zaczynamy od głowy while(tmp->wsk!= elem) //przejście do elementu wskazującego na elem tmp=tmp->wsk; tmp->wsk = elem->wsk; //zmiana powiązania free(elem); //zwolnienie pamięci usunięcie elementu else printf("nie mogę usunąć lista pusta lub brak pogo elementu"); tmp elem

/* funkcja dodaje nowy element do listy tak, aby była uporządkowana rosnąco według pola z danymi */ void in_order(int data) struct lista *tmp, *poprzedni; tmp = head; while (tmp-> < data) poprzedni = tmp; tmp = tmp->wsk; in(data,poprzedni); //zmienne pomocnicze //zaczynamy od początku listy //warunek pętli może dotyczyć innego pola //zapamiętanie elementu //przejście do następnego elementu //dodanie elementu w odpowiednim miejscu

void print_list(void) //analogicznie jak w przypadku kolejki struct list *tmp; tmp = head; while(tmp!= NULL) printf("element listy: %d", tmp->); tmp = tmp->wsk; void empty_list(void) //usunięcie wszystkich elementów listy while (head!= NULL) out(head); //w kolejce "głowa" jest usuwana domyślnie //w liście nie

Lista jednokierunkowa cykliczna sąsiadem ostatniego elementu jest element pierwszy funkcję elementu head może pełnić dowolny element w przypadku listy jednoelementowej, element wskazuje sam na siebie

Lista dwukierunkowa każdy element, oprócz elementów skrajnych, "zna" obu swoich sąsiadów elementy skrajne mają i "znają" tylko jednego sąsiada nie ma konieczności "pamiętania" pierwszego (head) lub ostatniego (tail) elementu listę dwukierunkową można łatwo przeszukiwać w obu kierunkach należy jednak "pamiętać" jeden z jej elementów

Lista dwukierunkowa Graficzna prezentacja listy dwukierunkowej in out NULL NULL NULL

Lista dwukierunkowa cykliczna Graficzna prezentacja listy dwukierunkowej cyklicznej

Implementacja listy dwukierunkowej w języku C struct lista int ; struct lista *pop; struct lista *nast; ; struct lista *head; //definicja pojedynczej struktury // mogą być innego typu, może być też więcej pól //wskaźnik wskazujący na poprzedni element //wskaźnik wskazujący na następny element //deklaracja elementu na początku listy ("głowa") // jest to zmienna globalna // zamiast "głowy" można pamiętać dowolny element // nie ma konieczności pamiętania "ogona" // wygodnie jest jednak pamiętać "głowę", "ogon" // i element bieżący

/* funkcja dodaje nowy element za elementem podanym jako parametr */ void in (int data, struct lista *elem) elem elem struct lista *tmp; tmp = malloc(sizeof(struct lista); tmp tmp-> = data; if (head!= NULL) //jeśli lista nie jest pusta if ((elem!= NULL) && (elem->nast!= NULL)) tmp->nast = elem->nast; elem->nast = tmp tmp->pop = elem; elem = tmp->nast; elem->pop = tmp; //wstawiamy w środku

if ((elem!= NULL) && (elem->nast == NULL)) //wstawiamy na końcu elem tmp tmp->nast = NULL; tmp->pop = elem; elem->nast = tmp; if (elem == NULL) //wstawiamy na początku HEAD tmp HEAD NULL tmp->nast = head; tmp->pop = NULL; head->pop = tmp; head = tmp; elem else NULL HEAD //jeśli lista była pusta head = tmp; //powstaje nowa "głowa" head->nast = head->pop = NULL; NULL NULL

/* funkcja usuwa z listy element podany jako parametr */ void out (struct lista *elem) struct lista *tmp; if ((head!= NULL) && (elem!= NULL)) //jeśli lista nie była pusta //i podano prawidłowy element if (elem == head) tmp= head->nast; free(head); head = tmp; head->pop=null; /jeśli to "głowa" ma być usunięta //ustawiamy tmp na element za "głową" //"głowa" jest usuwana zwolnienie pamięci //zmienna tymczasowa staje się nową "głową" //"głowa" nie ma elementu poprzedzającego

if (elem->nast == NULL) //jeśli jest to ostatni element tmp=elem->pop; //ustawiamy tmp na poprzedni element free(elem); //zwolnienie pamięci usunięcie elementu tmp->nast = NULL; //ostatni element nie ma następnika else //jeśli to element w środku listy tmp=elem->pop; //ustawiamy tmp na poprzedni element tmp->nast = elem->nast; //wiążemy tmp z kolejnym elementem free(elem); //zwolnienie pamięci usunięcie elementu elem = tmp->nast; //używamy zmiennej elem jako pomocniczej elem->pop = tmp; //aby powiązać ze sobą elementy else printf("nie mogę usunąć lista pusta lub brak pogo elementu");

Lista dwukierunkowa z jednym wskaźnikiem taka realizacja jest możliwa, gdy: wskaźnik można traktować jako liczbę i wykonywać na niej działania bitowe wskaźnik pusty równy jest zero w takim przypadku pojedynczy wskaźnik zawiera różnicę symetryczną (alternatywę rozłączną) [funkcja xor] wartości liczbowej wskaźników na element poprzedni i następny podczas przechodzenia listy należy przechowywać adres poprzednio odwiedzonego elementu na podstawie własności A (A B) = B można z zakodowanej liczby wyciągnąć poprzedni lub następny element, w zależności od kierunku przeglądania listy

Drzewo (tree) z matematycznego punktu widzenia drzewo jest to graf spójny i acykliczny spójny z każdego wierzchołka drzewa można dotrzeć do każdego innego wierzchołka acykliczny bez cykli; dowolne dwa wierzchołki można połączyć tylko w jeden sposób Przykłady drzew:

Drzewo (tree) w informatyce jest to struktura danych, reprezentująca drzewo matematyczne w naturalny sposób odzwierciedla hierarchię danych drzewa ułatwiają i przyspieszają wyszukiwanie danych oraz ułatwiają zarządzanie posortowanymi danymi zastosowanie: bazy danych, grafika komputerowa, przetwarzanie tekstu, telekomunikacja

Budowa drzewa drzewo składa się z węzłów (node) oraz krawędzi (branch), które je łączą węzeł początkowy (najwyższy) jest korzeniem drzewa (root) węzeł nie mający potomków (węzłów podrzędnych) nazywany jest liściem (leaf) węzeł nie będący ani korzeniem ani liściem nazywamy węzłem wewnętrznym ciąg krawędzi łączących węzły nazywamy ścieżką (path) wysokość drzewa to długość najdłuższej ścieżki prowadzącej od korzenia do liścia

Podstawowe operacje na drzewie dodanie nowego elementu w określonym miejscu drzewa usunięcie określonego elementu z drzewa wyliczenie (wypisanie) wszystkich elementów drzewa wyszukanie określonego elementu w drzewie Metody przechodzenia po drzewie preorder najpierw rodzic, następnie potomkowie postorder najpierw potomkowie, następnie rodzic inorder potomek, rodzic, kolejny potomek (drzewa binarne)

Drzewo binarne drzewo binarne to drzewo, którego każdy węzeł ma co najwyżej dwóch potomków drzewo binarne jest dobrym narzędziem wspomagającym sortowanie oraz wyszukiwanie danych przy użyciu rekurencji drzewo binarne może być: pełne każdy węzeł ma dokładnie dwóch potomków kompletne każdy liść ma tę samą głębokość

W kompletnym drzewie binarnym mamy węzłów na poziomie d (d>=0): 2 d węzłów wewnętrznych: 2 d -1 (1+2+4+ +2 d-1 ) wszystkich węzłów: 2 d+1-1 (1+2+4+ +2 d ) Implementacja drzewa za pomocą tablicy: 4 2 1 3 5 6 7 0 1 2 1 2 3 4 5 6 7 2 0 2 1 2 2 rodzic(i)=floor(i/2) lewy_syn(i)=2*i prawy_syn(i)=2*i+1

Implementacja drzewa za pomocą struktury: wskaźniki do rodzica i potomków struct drzewo int ; struct drzewo *przodek; struct drzewo *lewy_syn; struct drzewo *prawy_syn; ; wskaźniki tylko do potomków struct drzewo int ; struct drzewo *lewy_syn; struct drzewo *prawy_syn; ; Zwykle wariant z dwoma wskaźnikami jest wystarczający.

Rekurencyjne przeglądanie drzew binarnych preorder (KLP: korzeń-lewe-prawe; sposób wzdłużny) void KLP(struct drzewo *node) if (node!= NULL) printf("dane z węzła: %d", node->); KLP(node->lewy_syn); KLP(node->prawy_syn);

Rekurencyjne przeglądanie drzew binarnych inorder (LKP: lewe-korzeń-prawe; sposób poprzeczny) void LKP(struct drzewo *node) if (node!= NULL) LKP(node->lewy_syn); printf("dane z węzła: %d", node->); LKP(node->prawy_syn);

Rekurencyjne przeglądanie drzew binarnych postorder (LPK: lewe-prawe-korzeń; sposób wsteczny) void LPK(struct drzewo *node) if (node!= NULL) LPK(node->lewy_syn); LPK(node->prawy_syn); printf("dane z węzła: %d", node->);

Drzewo binarne zorganizowane (uporządkowane) drzewo, w którym dla każdego węzła wszystkie klucze jego lewego poddrzewa są mniejsze od klucza go węzła, a prawego poddrzewa - większe ten typ drzewa znacznie przyspiesza wyszukiwanie danych (dużo mniejsza złożoność obliczeniowa)

Zastosowanie drzew binarnych do obsługi ONP wyrażenie w notacji nawiasowej (c + b) * a + (e / d) wyrażenie w notacji ONP a b c + * d e / + Drzewo można wykorzystać do "rozpięcia" na nim wyrażenia w notacji ONP, a następnie obliczenia wartości tego wyrażenia. + Wyrażenie "rozpięte" na drzewie * / a + d e b c

Algorytm "rozpinania" wyrażenia w ONP na drzewie Elementy czytamy kolejno od końca 1. Pierwszy pobrany element stanowi korzeń drzewa 2. Kolejny element stanowi jego potomka z prawej strony 3. Jeżeli wstawiany element jest: a) znakiem należy powtórzyć punkt 2 b) cyfrą (liczbą) lub literą (zmienną) należy wrócić do najbliższego węzła (cofnąć się o jeden poziom) i sprawdzić, czy ma on już potomka z lewej strony * jeśli nie ma należy go utworzyć i powtórzyć punkty 2 i 3 * jeśli ma wrócić do najbliższego węzła (wyżej) nie posiadającego dwóch potomków i powtórzyć punkty 2 i 3 Wszystkie operacje wykonujemy do wyczerpania elementów.

Obliczanie wartości wyrażenia w ONP "rozpiętego" na drzewie 1. zaczynamy od najniższego poziomu (poddrzewa) lewego potomka korzenia 2. wartość poddrzewa obliczamy od prawej do lewej, czyli wartość prawego potomka, znak operacji w węźle i wartość lewego potomka da nam wyrażenie arytmetyczne do obliczenia 3. usuwamy obu potomków węzła, a jako nową wartość węzła wstawiamy wynik operacji 4. przechodzimy na kolejny (wyższy) poziom 5. gdy dojdziemy do potomka korzenia, powtarzamy punkty 1-4 dla prawego potomka korzenia 6. na koniec obliczamy wartość dla operacji, której znak zapisany jest w korzeniu

Przykład "rozpinania" na drzewie i obliczania wartości wyrażenia w Odwrotnej Notacji Polskiej wyrażenie w notacji ONP 4 2 3 + * 1 5 - + rozpinanie + * - obliczanie + 24 * 20-4 4 + 1 5 4 + 5 1 5 2 3 2 3

Zastosowanie drzewa do kodowania algorytmem Huffmana w roku 1952 Dawid Huffman opracował metodę konstrukcji kodu o zmiennej długości słów kodowych kodowanie opiera się na częstości występowania znaków kod przedstawiony jest w postaci drzewa binarnego jest to drzewo pełne i kompletne jeżeli A jest rozmiarem alfabetu, to drzewo ma A liści i A -1 węzłów wewnętrznych drzewo budujemy od dołu, od liści każdy liść oznaczony jest cyfrą 0 lub 1, a jego umiejscowienie zależy od częstości występowania znaku, który przechowuje

Przykład: kodujemy tekst "eleteleenerget" Częstości poszczególnych liter: e 7 l 2 t 2 n 1 r 1 g 1 Procedura: a) po określeniu częstości znaków tworzymy dla każdego znaku węzeł b) następnie powtarzamy: - tworzymy nowy węzeł dla najrzadziej występujących znaków - oznaczamy gałęzie jako 0 i 1 1 14 0 9 5 1 0 0 1 2 3 1 0 1 0 r 1 1 7 2 1 2 g e l n t

Przykład: kodujemy tekst "eleteleenerget" Kody poszczególnych liter (odczytane wzdłuż ścieżek w drzewie): e 10 l 01 t 000 n 001 r 111 g 110 Zakodowany tekst wejściowy: 100110000100110100011011111010000

Dziękuję za uwagę. Kolejny wykład: 1 czerwca 2016 Zapraszam!