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

Podobne dokumenty
Struktury dynamiczne

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

Struktury danych: stos, kolejka, lista, drzewo

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

Dynamiczne struktury danych

ALGORYTMY I STRUKTURY DANYCH

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

Listy, kolejki, stosy

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

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

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

Algorytmy i Struktury Danych

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

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

E S - uniwersum struktury stosu

Wstęp do programowania

Podstawy Informatyki. Wykład 6. Struktury danych

Algorytmy i struktury danych. wykład 5

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

Programowanie obiektowe

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

Struktury Danych i Złożoność Obliczeniowa

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

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

Stos LIFO Last In First Out

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

Abstrakcyjne struktury danych - stos, lista, drzewo

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)

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

Programowanie i struktury danych

ALGORYTMY I STRUKTURY DANYCH

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

Struktury. Przykład W8_1

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Lista liniowa dwukierunkowa

Wstęp do programowania

INFORMATYKA W SZKOLE. Podyplomowe Studia Pedagogiczne. Dr inż. Grażyna KRUPIŃSKA. D-10 pokój 227

Teoretyczne podstawy informatyki

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

Wykład 5 Wybrane zagadnienia programowania w C++ (c.d.)

Podstawy informatyki 2

ZASADY PROGRAMOWANIA KOMPUTERÓW

Podstawy Programowania. Listy i stosy

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

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

Drzewa poszukiwań binarnych

KURS C/C++ WYKŁAD 6. Wskaźniki

INFORMATYKA DANE.

Programowanie i struktury danych 1 / 44

Tadeusz Pankowski

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

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

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

Wskaźniki i dynamiczna alokacja pamięci. Spotkanie 4. Wskaźniki. Dynamiczna alokacja pamięci. Przykłady

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

Laboratorium nr 9. Temat: Wskaźniki, referencje, dynamiczny przydział pamięci, tablice dynamiczne. Zakres laboratorium:

Ćwiczenie 7 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

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

Strategia "dziel i zwyciężaj"

Wykład 3 Składnia języka C# (cz. 2)

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Programowanie obiektowe i C++ dla matematyków

Podstawowe struktury danych


Algorytmy i Struktury Danych

Wskaźniki. Programowanie Proceduralne 1

Lista 5 Typy dynamiczne kolejka

Wykład 6_1 Abstrakcyjne typy danych stos Realizacja tablicowa i za pomocą rekurencyjnych typów danych

Zasady programowania Dokumentacja

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

. Podstawy Programowania 2. Jednokierunkowa lista liniowa. Arkadiusz Chrobot. 28 marca 2017

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:

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

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

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

Lab 9 Podstawy Programowania

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Poprawność semantyczna

Teoretyczne podstawy informatyki

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

Lista, Stos, Kolejka, Tablica Asocjacyjna

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

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

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

Kiedy potrzebne. Struktura (rekord) Struktura w języku C# Tablice struktur. struktura, kolekcja

Programowanie komputerów. Jacek Lach Zakład Oprogramowania Instytut Informatyki Politechnika Śląska

Struktury Danych i Złożoność Obliczeniowa

WSTĘP DO INFORMATYKI. Struktury liniowe

Wykład 1: Wskaźniki i zmienne dynamiczne

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

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

Algorytmy i Struktury Danych

Podstawy programowania skrót z wykładów:

Zmienne i struktury dynamiczne

Podstawy Programowania 2 Jednokierunkowa lista liniowa. Plan. Jednokierunkowa lista liniowa. Jednokierunkowa lista liniowa. Notatki. Notatki.

Wykłady opracowane zostały w oparciu o materiały prof. dr hab. Jerzego Weresa. Poznań 2009/2010 Algorytmy i struktury danych Krzysztof Nowakowski 2

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

6 Przygotował: mgr inż. Maciej Lasota

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

Transkrypt:

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

INFORMATYKA Temat: Struktury dynamiczne Wykład 7 Struktury dynamiczne lista jednokierunkowa, lista dwukierunkowa, listy cykliczne, stos, kolejka, drzewa. W większości złożonych programów wykorzystuje się pewne struktury przechowywane na tzw. stercie (obszar pamięci zajmowany przez kod wynikowy i zmienne globalne a stos). Struktury te nazywane są strukturami dynamicznymi, ponieważ ich wielkość i stopień złożoności zmienia się w czasie działania programu. Do struktur dynamicznych zalicza się listy, stosy, kolejki i drzewa. 1. Lista jednokierunkowa Lista jednokierunkowa zawiera pewne dane (elementy listy) oraz wskaźnik na ępny element listy. Ostatni element listy jednokierunkowej ma wskaźnik ustawiony na NULL. Dostęp do listy umożliwia wskaźnik ustawiony na jej pierwszy element (na Rys. 1 jest to wskaźnik pocz ). Przeglądania listy i pobieranie jej elementów możliwe jest dzięki operatorowi wskaźnikowemu ->. Rys. 1. N-elementowa lista jednokierunkowa Elementy listy jednokierunkowej są strukturami. W języku ANSI C/C++ jeden element listy jednokierunkowej ma ępującą postać: 96

struct Lista_jednokierunkowa { int dana_1;... int dana_n; struct Lista_jednokierunkowa *; }; Przykład 87. W przykładzie ępuje wygenerowanie listy jednokierunkowej, ze wstawianiem elementów na początku listy. 1) Następuje zdefiniowanie struktury o dwóch polach dane_1 i dane_2 oraz zmiennej wskaźnikowej wskazującej na ępny element listy. struct Element_listy_jednokierunkowej { int dana_1; int dana_2; struct Element_listy_jednokierunkowej *; }; Rys. 2. Budowa elementu listy jednokierunkowej 2) Następuje deklaracja dwóch zmiennych wskaźnikowych pocz i wsk pomocnych przy operowaniu na elementach listy. struct Element_listy_jednokierunkowej *pocz=null, *wsk=null; 3) Następuje dynamiczne przydzielenie pamięci dla zmiennej wskaźnikowej pocz. Tym samym zostaje utworzy pierwszy element listy jednokierunkowej o polach zdefiniowanej struktury. Wskaźnik pocz zostaje ustawiony na tym elemencie listy. pocz=malloc(sizeof(struct Element_listy_jednokierunkowej)); 97

pocz dane_1 dane_2 Rys. 3. Deklaracja elementu struktury 3) Zgodnie z definicją listy jednokierunkowej ępuje przypisanie wartości NULL wskaźnikowi pola struktury. pocz -> = NULL; Rys. 4. Tworzenie listy jednokierunkowej 4) Następuje zadanie wartości liczbowych polom dane_1 i dane_2. Np.: pocz->dana_1=1; pocz->dana_2=1; 98

Rys. 5. Tworzenie listy jednokierunkowej 5) Następuje utworzenie kolejnego elementu listy. Wskaźnik wsk pokazuje na nowo utworzony element listy. wsk=malloc(sizeof(struct Element_listy_jednokierunkowej)); Rys. 6. Tworzenie listy jednokierunkowej 6) Następuje przypisanie wartości NULL wskaźnikowi wsk oraz zadanie wartości liczbowych polom dane_1 i dane_2 nowego elementu listy. wsk->=null; wsk->dana_1=2; wsk->dana_2=2; Rys. 7. Tworzenie listy jednokierunkowej 99

7) Następuje połączenie elementów listy. Wskaźnik wsk pokazywać będzie na element listy który jako pierwszy został zdeklarowany. Wskaźnik wsk nie jest przesuwany a jedynie pokazuje na element listy, który jako pierwszy został zdeklarowany na liście. wsk->=pocz; Rys. 8. Tworzenie listy jednokierunkowej 8) Następuje przesunięcie wskaźnika pocz tak, że będzie teraz ustawiony i jednocześnie będzie pokazywał na element listy który jako drugi został zadeklarowany. pocz = wsk; Rys. 9. Tworzenie listy jednokierunkowej 9) Proces tworzenia listy jednokierunkowej metodą wstawiania elementów na początku listy został zakończony. Rys. 10. Zakończenie procesu tworzenia listy jednokierunkowej 100

Poniżej przedstawiono operację wyświetlenia elementów listy. Po wyświetleniu listy ępuje zwolnienie obszaru pamięci zajmowanego przez zmienną wsk. while(wsk!=0) { printf("%d %d\n",wsk->dana_1, wsk->dana_2); wsk=wsk->; } free(wsk); Uwaga 9! Przy zapisie while(wsk!=0) wskaźnik wsk przyjmie po zakończeniu działania pętli wartość 0, a nie ustawi się na ostatnim elemencie listy. Przykład 88. W przykładzie ępuje wygenerowanie listy jednokierunkowej ze wstawianiem elementów od końca listy. void Push(int liczba){ struct Element_listy_jednokierunkowej *pocz=null, *wsk=null, *pom=null; } pocz=malloc(sizeof(struct Element_listy_jednokierunkowej)); pocz->dana_1=liczba; pocz->dana_2=liczba; pocz->=null; wsk=malloc(sizeof(struct Element_listy_jednokierunkowej)); wsk->dana_1=liczba+1; wsk->dana_2=liczba+1; wsk->=null; /*wskaznik "pocz" bedzie pokazywal na drugi element listy*/ pocz->=wsk; /*wskaznik "pocz" bedzie pokazywal na pierwszy zdeklarowany element listy*/ pom=pocz; /*wyswetlenie elementow listy za pomoca petli*/ while(pom!=0) { printf("%d %d\n",pom->dana_1, pom->dana_2); pom=pom->; } free(wsk); free(pocz); free(pom); 101

2. Lista dwukierunkowa Lista dwukierunkowa zawiera pewne dane (elementy listy) oraz wskaźniki na ępny i poprzedni element listy. Wskaźniki na ępny element listy ostatniego elementu listy oraz wskaźnik na poprzedni element listy pierwszego elementu listy mają wartość NULL. Dostęp do listy umożliwia wskaźnik ustawiony na jej pierwszym elemencie (na Rys. 1 jest to wskaźnik pocz ). Możliwe jest również zastosowanie wskaźnika ustawionego na ostatnim elemencie listy dwukierunkowej, przeglądanie listy można wówczas wykonywać od przodu lub od tyłu. Rys. 11. N-elementowa lista dwukierunkowa Elementy listy dwukierunkowej są strukturami. W języku ANSI C/C++ jeden element listy dwukierunkowej ma ępującą postać: struct Lista_dwukierunkowa { typ_danej dana_1;... typ_danej dana_n; struct Lista_dwukierunkowa *, *pop; }; Przykład 89. W przykładzie ępuje wygenerowanie listy dwukierunkowej, ze wstawianiem elementów na początku listy. 1) Następuje zdefiniowanie struktury o dwóch polach dane_1 i dane_2 oraz zmiennych wskaźnikowych i pop wskazujących na ępny i poprzedni element listy. struct Element_listy_dwukierunkowej { int dana_1; int dana_2; struct Element_listy_jednokierunkowej *, *pop; }; 102

Rys. 12. Budowa elementu listy dwukierunkowej 2) Następuje deklaracja dwóch wskaźników pocz i wsk i jednocześnie elementów listy. Elementom listy zostają zadane wszystkie wartości początkowe. struct Element_listy_dwukierunkowej *pocz=null, *wsk=null; pocz=malloc(sizeof(struct Element_listy_dwukierunkowej)); pocz->dana_1=1; pocz->dana_2=1; pocz->=null; pocz->pop=null; wsk=malloc(sizeof(struct Element_listy_dwukierunkowej)); wsk->dana_1=2; wsk->dana_2=2; wsk->=null; wsk->pop=null; Rys. 13. Budowa listy dwukierunkowej 3) Następuje połączenie elementów listy oraz przesunięcie wskaźnika pocz na początek listy. Po wyświetleniu listy wymyty zostaje wskaźnik wsk. wsk->=pocz; pocz->pop=wsk; pocz=wsk; while(wsk!=0) { printf("%d %d\n",wsk->dana_1, wsk->dana_2); wsk=wsk->; } free(wsk); 103

Uwaga 10! Przy zapisie while(wsk!=0) wskaźnik wsk przyjmuje po zakończeniu działania pętli wartość 0, a nie ustawi się na ostatnim elemencie listy. Rys. 14. Budowa listy dwukierunkowej 4) Proces tworzenia listy dwukierunkowej metodą wstawiani elementów na początku listy został zakończony. Przykład 90. W przykładzie ępuje wygenerowanie listy dwukierunkowej, ze wstawianiem elementów od końca listy. void Push(int liczba) { struct Element_listy_dwukierunkowej *pocz=null, *wsk=null; } pocz=malloc(sizeof(struct Element_listy_dwukierunkowej)); pocz->dana_1=liczba; pocz->dana_2=liczba; pocz->=null; pocz->pop=null; wsk=malloc(sizeof(struct Element_listy_dwukierunkowej)); wsk->dana_1=liczba+1; wsk->dana_2=liczba+1; wsk->=null; wsk->pop=null; /*polaczenie elementow listy (dolaczenie elementu na koncu listy)*/ pocz->=wsk; wsk->pop=pocz; /*przesuniecie wskanika "wsk" na poczatek listy - konieczne ze wzgledu na pozniejsze przegladanie listy*/ wsk=pocz; while(wsk!=0) { printf("%d %d\n",wsk->dana_1, wsk->dana_2); wsk=wsk->; } free(wsk); free(pocz); 104

Przykład 91. W przykładzie przedstawiono sposób usuwania wybranego elementu z 3-elementowej listy dwukierunkowej. 1) Utworzenie 3-elementowej listy dwukierunkowej: /*utworzenie 1-go elementu listy*/ pocz=malloc(sizeof(struct Element_listy_dwukierunkowej)); pocz->dana_1=liczba; pocz->dana_2=liczba; pocz->=null; pocz->pop=null; /*utworzenie 2-go elementu listy*/ wsk1=malloc(sizeof(struct Element_listy_dwukierunkowej)); wsk1->dana_1=liczba+1; wsk1->dana_2=liczba+1; wsk1->=null; wsk1->pop=null; /*utworzenie 3-go elementu listy*/ wsk2=malloc(sizeof(struct Element_listy_dwukierunkowej)); wsk2->dana_1=liczba+2; wsk2->dana_2=liczba+2; wsk2->=null; wsk2->pop=null; /*polaczenie elementow listy (dolaczenie elementów na koncu listy)*/ pocz->=wsk1; wsk1->pop=pocz; wsk1->=wsk2; wsk2->pop=wsk1; Rys. 15. Budowa 3-elementowej listy dwukierunkowej 105

2) Usunięcie 2-go elementu listy, połączenie 1-go i trzeciego elementu listy: wsk1->=null; wsk1->pop=null; pocz->=null; wsk2->pop=null; free(wsk1); pocz->=wsk2; wsk2->pop=pocz; Rys. 16. Usuniecie 2-go elementu listy dwukierunkowej 106

3. Lista cykliczna Wyróżniamy listy cykliczne jednokierunkowe i dwukierunkowe. W listach cyklicznych wskaźnik na ępny element w ostatnim elemencie nie ma wartości NULL, ale wskazuje na początek listy. W przypadku list cyklicznych dwukierunkowych, wskaźnik na element poprzedni w pierwszym elemencie listy wskazuje na element ostatni, zaś wskaźnik elementu ostatniego listy pokazuje na pierwszy element tej listy (Rys.18). Listy cykliczne nie posiadają początku i końca. Pozycję na liście określa zewnętrzny wskaźnik, którego położenie może się dowolnie zmieniać. Struktury w języku ANSI C/C++ odpowiadające elementom list cyklicznych są takie same jak odpowiadających im list zwykłych. Struktura logiczna listy cyklicznej jednokierunkowej: wsk (1) (2) (3) (N) Rys. 17. Struktura listy cyklicznej jednokierunkowej Struktura logiczna listy cyklicznej dwukierunkowej: Rys. 18. Struktura listy cyklicznej dwukierunkowej 107

4. Stos Stos jest strukturą często używaną w programowaniu. Stos bywa również nazywany kolejką LIFO (Last In First Out). Elementy ostatnio położone na stosie są z niego zdejmowane przed elementami położonymi wcześniej. Do obsługi stosu służy zmienna nazywana wskaźnikiem stosu (na Rys. 19 jest to wsk_stosu ). Wskaźnik stosu może pokazywać ostatnio położony element lub pierwsze wolne miejsce na stosie. Ze stosu zdejmowany jest element, na który pokazuje wskaźnik stosu. ADRES PAMIĘCI 7048 7049 7410 STOS 5 wsk_stosu Rys. 19. Struktura stosu Stos może być implementowany za pomocą tablicy jednowymiarowej. Wskaźnikiem stosu jest wtedy zmienna będąca indeksem w tablicy wskazująca ostatnio położony na stosie element. Stos musi zapewniać poprawne wykonanie przynajmniej dwóch funkcji: Push (element) - położenie elementu na stosie, Pop() - zdjęcie elementu z wierzchołka stosu. Stos może być również implementowany przy użyciu listy jednokierunkowej. Szczyt stosu znajduje się wtedy na początku listy. Nowe elementy kładzione na stos są zawsze dołączane na początek listy. Elementy zdejmowane są zawsze z początku (wierzchołka) stosu. Przykład 92. stosu. W przykładzie pokazano zastosowanie listy jednokierunkowej do zaimplementowania 1) Następuje wygenerowanie stosu oraz ustawienie wskaźnika wsk_stos na wierzchołek stosu. pierwszy=malloc(sizeof(struct Element_listy_jednokierunkowej)); pierwszy->dana=1; pierwszy->=null; drugi=malloc(sizeof(struct Element_listy_jednokierunkowej)); drugi->dana=2; drugi->=null; trzeci=malloc(sizeof(struct Element_listy_jednokierunkowej)); trzeci->dana=3; trzeci->=null; 108

/*polaczenie elementow stosu*/ pierwszy->=drugi; drugi->=trzeci; /*ustawienie wskaznika "wsk_stos" na wierzcholek stosu*/ wsk_stos=pierwszy; wsk_stos 1 2 3 pierwszy drugi trzeci Rys. 20. Organizacja 3-elementowego stosu 2) Zdjęcie elementu ze stosu (pop()). wsk_stos=drugi; //drugi z elementow jest teraz na wierzcholku stosu pierwszy->=null; free(pierwszy); Rys. 21. Zdjęcie elementu ze stosu 109

3) Położenie elementu na stosie(push(element)). czwarty=malloc(sizeof(struct Element_listy_jednokierunkowej)); czwarty->dana=4; czwarty->=null; czwarty->=drugi; wsk_stos=czwarty; //przesuniecie wskaznika "wsk_stos" na //wierzcholek stosu wsk_stos 4 2 3 NULL pierwszy (czwarty) drugi (drugi) trzeci (trzeci) Rys. 22. Wstawienie elementu na stos 110

5. Kolejka Kolejka jest dynamiczną strukturą danych typu FIFO (First In, First Out). Kolejka może być implementowana na liście jednokierunkowej przy pomocy dwóch wskaźników. Na Rys. 23 wskaźnik pocz_kol pokazuje na początek listy (jest to równocześnie wskaźnik na pierwszy element, który zostanie z kolejki pobrany) natomiast wskaźnik kon_kol pokazuje na koniec listy (jest to miejsce, gdzie będą wstawiane elementy dopisywane do kolejki). pocz_kol kon_kol (1) (2) (3) (N) Rys. 23. Organizacja kolejki NULL Przykład 93. W przykładzie pokazano zastosowanie listy jednokierunkowej do zaimplementowania kolejki. 1) Następuje wygenerowanie i połączenie elementów kolejki oraz ustawienie wskaźników pocz_kol i kon_kol na początek i koniec kolejki. pierwszy=malloc(sizeof(struct Element_listy_jednokierunkowej)); pierwszy->dana_1=1; pierwszy->dana_2=1; pierwszy->=null; drugi=malloc(sizeof(struct Element_listy_jednokierunkowej)); drugi->dana_1=2; drugi->dana_2=2; drugi->=null; trzeci=malloc(sizeof(struct Element_listy_jednokierunkowej)); trzeci->dana_1=3; trzeci->dana_2=3; trzeci->=null; /*polaczenie elementow kolejki*/ pierwszy->=drugi; drugi->=trzeci; /*ustawienie wskaznikow "pocz_kol" i "kon_kol" na poczatek i koniec kolejki*/ pocz_kol=pierwszy; kon_kol=trzeci; 111

pocz_kol kon_kol 1 1 2 2 3 3 NULL pierwszy drugi trzeci Rys. 24. Organizacja 3-elementowej kolejki 2) Następuje zdjęcie pierwszego elementu z kolejki (zwolnienie wskaźników, przesunięcie pocz_kol na drugi element kolejki oraz wymycie pierwszego elementu kolejki) i dostawienie nowego elementu na koniec kolejki. Drugi element starej kolejki staje się pierwszym elementem nowej kolejki. czwarty=malloc(sizeof(struct Element_listy_jednokierunkowej)); czwarty->dana_1=4; czwarty->dana_2=4; czwarty->=null; pocz_kol=drugi; //drugi z elementow jest teraz pierwszy w kolejce pierwszy->=null; free(pierwszy); trzeci->=czwarty; //na koncu kolejki dochodzi nowy element Rys. 25. Organizacja 3-elementowej kolejki, dojście nowego elementu 112

6. Drzewa Drzewa są strukturami hierarchicznymi i rekurencyjnymi. Można je podzielić na drzewa mnogościowe (każdy węzeł może mieć dowolną ilość potomków) lub drzewa binarne, gdzie każdy węzeł ma nie więcej niż dwóch potomków. root wsk_root wsk_lewy wsk_prawy syn_l syn_p wsk_lewy wsk_prawy wsk_lewy wsk_prawy NULL NULL NULL NULL NULL NULL NULL NULL Rys. 26. Struktura typowego drzewa binarnego Wskaźnik (wsk_root) umożliwiający dostęp do całego drzewa wskazuje na węzeł, który nie jest niczyim potomkiem. Węzeł ten nazywa się korzeniem drzewa (root). Każdy węzeł drzewa binarnego ma co najwyżej dwóch potomków - synów: lewego (syn_l) i prawego (syn_p). Jeśli pewien węzeł nie ma jednego z potomków to odpowiedni wskaźnik jest ustawiony na NULL. Rekurencyjność struktury drzewiastej polega na tym, że każdy węzeł może być rozpatrywany jako drzewo (część zaznaczona w kółku może być potomkiem korzenia), ale również może być traktowany tak jak osobne drzewo binarne. Stąd algorytmy operujące na strukturach drzewiastych są najczęściej funkcjami rekurencyjnymi. 113

Elementy drzewa są strukturami. W języku ANSI C/C++ jeden element drzewa ma ępującą postać: struct Elementy_drzewa { typ_danej dana_1;... typ_danej dana_n; struct Elementy_drzewa *wsk_lewy, *wsk_prawy; }; Drzewa binarne stosowane są do przechowywania danych. Dana przechowywane są w węzłach drzewa. Pierwsza informacja jest wstawiana jako korzeń drzewa. Kolejne dane są wstawiane tak, że w każdym węźle dane mniejsze lub równe od danej w tym węźle znajdują się na lewo, natomiast większe - na prawo. Taka organizacja przyspiesza znacznie wyszukiwanie elementów zapisanych w drzewie, ponieważ chcąc sprawdzić czy dana informacja istnieje, nie sprawdza się wszystkich elementów, ale tylko jedną ścieżkę w drzewie. Przykład 94. W przykładzie przedstawiono funkcję tworzącą drzewo binarne oraz funkcję kasującą drzewo binarne. struct drzewo { char dana[100]; struct drzewo *lewy, *prawy; }; /*wstawianie elementow do drzewa, w celu przechowania historii dodawanych elementow stosuje się wskaznik na wskaznik*/ void f_puts_string(struct drzewo **root, char * name) { if (*root == NULL) { *root = malloc(sizeof(struct dzewo)); if (* root == NULL) { printf("blad przydzialu pamieci!\n"); exit(1); } /*wpisanie danych do kolejnego korzenia*/ strcpy((* root) -> dana, name); (* root) -> lewy = (* root) -> prawy = NULL; } /*porownanie wprowadzonych napisow, dluzszy od poprzedniego dorzucony bedzie na prawo od swojego korzenia, krotszy na lewo*/ if (strcmp((*root) -> dana, name) > 0) f_puts_string(& (*root) -> prawy, name); //rekurencja dla //kazdego liscia ktory //jest jednoczesnie //korzeniem else f_puts_string(& (*root) -> left, name); } 114

/*kasowanie drzewa*/ void Usun_drzewo(struct drzewo *root) { if (root == NULL) return; Usun_drzewo (root -> lewy); //rekurencja w celu ustalenia //polozenia elementu w drzewie Usun_drzewo (root -> prawy; free(root); } Np. Wprowadzamy kolejno słowa { domdom, domd, dom, domdo } wówczas drzewo binarne wprowadzonych słów będzie miało ępującą postać: root lewy domdom prawy domd NULL dom domdo NULL NULL NULL NULL Rys. 27. Przykład drzewa binarnego słów 115