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

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

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

Wstęp do programowania

Wskaźniki. Programowanie Proceduralne 1

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

E S - uniwersum struktury stosu

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

Języki i metodyka programowania. Wskaźniki i tablice.

Wstęp do programowania INP001213Wcl rok akademicki 2018/19 semestr zimowy. Wykład 4. Karol Tarnowski A-1 p.

Zmienne i struktury dynamiczne

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

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 i struktury danych. Wykład 4 Dr Piotr Cybula

Dynamiczne struktury danych

Lab 9 Podstawy Programowania

Podstawy Programowania 2 Dwukierunkowa lista liniowa. Plan. Wstęp. Implementacja. Notatki. Notatki. Notatki. Notatki.

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

ZASADY PROGRAMOWANIA KOMPUTERÓW

Podstawy programowania w języku C++

ALGORYTMY I STRUKTURY DANYCH

Wykład 1: Wskaźniki i zmienne dynamiczne

Programowanie i struktury danych

Strategia "dziel i zwyciężaj"

Podstawy programowania w języku C++

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

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

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

Sortowanie przez wstawianie Insertion Sort

Podstawowe struktury danych

Wskaźniki. Informatyka

Algorytmy i Struktury Danych.

. Podstawy Programowania 2. Dwukierunkowa lista liniowa. Arkadiusz Chrobot. 7 kwietnia 2019

Podstawy programowania. Wykład: 8. Wskaźniki. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

Wykład 7 Abstrakcyjne typy danych słownik (lista symboli)

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

Algorytmy i język C++

- - Ocena wykonaniu zad3. Brak zad3

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

TEMAT : KLASY DZIEDZICZENIE

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

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:

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

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Wykład 8: klasy cz. 4

Typy złożone. Struktury, pola bitowe i unie. Programowanie Proceduralne 1

Wskaźniki w C. Anna Gogolińska

DYNAMICZNE PRZYDZIELANIE PAMIECI

Lista dwukierunkowa - przykład implementacji destruktorów

Podstawy programowania. Wykład 6 Złożone typy danych: struktury, unie. Krzysztof Banaś Podstawy programowania 1

Szablony klas, zastosowanie szablonów w programach

Programowanie obiektowe i C++ dla matematyków

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

Algorytmy i Struktury Danych.

WYKŁAD 9. Algorytmy sortowania elementów zbioru (tablic) Programy: c4_1.c... c4_3.c. Tomasz Zieliński

1. Nagłówek funkcji: int funkcja(void); wskazuje na to, że ta funkcja. 2. Schemat blokowy przedstawia algorytm obliczania

1 Wskaźniki i listy jednokierunkowe

Programowanie komputerowe. Zajęcia 1

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

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Deklaracja struktury w C++

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Programowanie komputerowe. Zajęcia 4

Tablice, funkcje - wprowadzenie

Algorytmy i Struktury Danych. Anna Paszyńska

Programowanie w VB Proste algorytmy sortowania

Podstawy Programowania C++

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

Jak napisać listę jednokierunkową?

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

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Wykład 4: Klasy i Metody

KURS C/C++ WYKŁAD 8. Deklaracja funkcji informuje komplilator jaką wartość funkcja będzie zwracała i jakiego typu są jej argumenty.

Algorytmy i struktury danych. wykład 5

Wybrane algorytmy tablicowe

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

Podstawy programowania w języku C++

Etap 2 - Budowa interfejsu. typedef struct ELEMENT* stos; struct ELEMENT { dane Dane; stos Nastepny; }; struct kolejka { stos Poczatek, Koniec; };

Dynamiczne struktury danych

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

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

Drzewa wyszukiwań binarnych (BST)

Język C, tablice i funkcje (laboratorium, EE1-DI)

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

main( ) main( void ) main( int argc, char argv[ ] ) int MAX ( int liczba_1, liczba_2, liczba_3 ) źle!

Wykład 5: Klasy cz. 3

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

Struktury. Przykład W8_1

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

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

Struktury Struktura polami struct struct struct struct

Zadanie 1 Przygotuj algorytm programu - sortowanie przez wstawianie.

Algorytmy i złożoności Wykład 5. Haszowanie (hashowanie, mieszanie)

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

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

C-struktury wykład. Dorota Pylak

TABLICE W JĘZYKU C/C++ typ_elementu nazwa_tablicy [wymiar_1][wymiar_2]... [wymiar_n] ;

Programowanie komputerowe. Zajęcia 3

Wstęp do wskaźników w języku ANSI C

Transkrypt:

Algorytmy i złożoności Wykład 3. Listy jednokierunkowe Wstęp. Lista jednokierunkowa jest strukturą pozwalającą na pamiętanie danych w postaci uporzadkowanej, a także na bardzo szybkie wstawianie i usuwanie elementów do i z listy. Pamiętana jest w postaci kontenerków zawierających porcję danych oraz wskaźnik (adres) następnego kontenerka. W ten sposób wystarczy pamięta wskaźnik do pierwszego elementu listy, by pamiętać całą listę. Aby zaimplementować listę jednokierunkową w języku C/C++ trzeba zdefiniować strukturę pełniącą rolę węzłów stanowiących kolejne elementy listy. Taka struktura składa się z dwóch części: pola (lub kilku pól), w którym pamiętane są przechowywane elementy ( użytkowe) oraz pola wskaźnikowego, w którym będzie pamiętany wskazanie do następnego węzła. Dostęp do całej listy wymaga utworzenia zmiennej wskaźnikowej, zawierającej wskazanie na pierwszy węzeł listy lub wartość jeśli lista list pusta (tzn. nie zawiera żadnych węzłów). Poczatek Rys. 1 Lista jednokierunkowa zawierająca 4 elementy. Poniżej przedstawiono przykładową definicję typów danych umożliwiających tworzenie listy jednokierunkowej, w której będą pamiętane typu Towar. Kolejne węzły listy będą zmiennymi dynamicznymi typu Wezel, Zmienna wskaźnikowa Poczatek może być wykorzystywana do pamiętania wskazania na pierwszy element listy. typedef struct Towar { char Nazwa[20]; float Cena; int Ilosc; Element; // definicja typu danych // przechowywanych w liście typedef struct Wezel { Element ; Wezel *nastepny; Wezel; Wezel *Poczatek = : Poniżej opisano typowe operacje wykonywane na elementach listy jednokierunkowej. 1

Dodawanie nowego elementu na początek listy. Podstawową operacją wykonywaną na liście jest dodawanie nowych elementów do listy. Trzeba wyróżnić dwa szczególne przypadki: dodawanie nowego elementu na początek listy oraz wstawianie nowego elementu za wskazanym elementem. Rysunek 2 ilustruje przypadek pierwszy: a) b) Rys. 2 Ilustracja dodawania nowego elementu na początek listy a) stan listy przed rozpoczęciem operacji, b) stan listy po zakończeniu operacji. Kolorem czerwonym zaznaczono nowy węzeł listy oraz nowe dowiązania, a numery na rysunku odpowiadają instrukcjom programu przedstawionego poniżej. int DodajNaPoczatek(Wezel *&, Element ) { Wezel *tmp; tmp = new Wezel; //(Wezel*)malloc(sizeof(Wezel)); if (tmp==) { printf( BRAK PAMIECI! ); return 1; tmp-> = ; tmp->nastepny = ; = tmp; return 0; Nagłówek funkcji (linia ) zawiera dwa parametry: oraz. Pierwszy parametr jest przekazanym przez referencję wskaźnikiem na początek listy. Drugi parametr zawiera, które mają być zapamiętane w nowym elemencie listy. W linii zadeklarowano pomocniczą zmienną wskaźnikową tmp, w której będzie pamiętany adres utworzonej zmiennej dynamicznej (instrukcja ) będącej nowym węzłem listy. Zmienną dynamiczną można utworzyć za pomocą operatora new (tylko w języku C++) lub za pomocą funkcji malloc (zob. komentarz). W linii sprawdzana jest poprawność alokacji pamięci. W instrukcji do pola w nowym węźle są kopiowane, które mają być przechowywane w liście. Instrukcje i dokonują modyfikacji dowiązań. Powyższa funkcja zwraca wartość 0 gdy operacja dodania elementu przebiega poprawnie oraz wartość 1 2

gdy element nie zostanie dodany z powodu braku pamięci na utworzenie zmiennej dynamicznej będącej nowym węzłem listy. Parametr musi być przekazany przez referencję, gdyż w wyniku poprawnego wykonania tej funkcji adres początkowego elementu listy ulegnie zmianie. Wstawianie nowego elementu za wskazanym elementem Czasami zachodzi konieczność wstawienia nowego elementu w środku lub na końcu listy. Wówczas miejsce wstawienia określa adres węzła, za którym ma być wstawiony nowy węzeł, który musi być pamiętany w jakiejś zmiennej wskaźnikowej. W poniższym przykładzie adres jest pamiętany w zmiennej biezacy. Ilustruje to rysunek 3. a) biezacy b) biezacy Rys. 3 Ilustracja wstawiania nowego elementu za węzłem wskazywanym przez zmienną biezacy a) stan listy przed rozpoczęciem operacji, b) stan listy po zakończeniu operacji. Kolorem czerwonym zaznaczono nowy węzeł listy oraz nowe dowiązania, a numery na rysunku odpowiadają instrukcjom programu przedstawionego poniżej. int DodajZaBiazacy(Wezel *biezacy, Element ) { Wezel *tmp; tmp = new Wezel; // (Wezel*)malloc(sizeof(Wezel)); if (tmp==) { printf( BRAK PAMIECI! ); return 1; tmp-> = ; // tu powinno być wpisanie danych tmp->nastepny = biezacy->nastepny; biezacy->nastepny = tmp; return 0; Nagłówek funkcji (linia ) zawiera dwa parametry: biezacy oraz. Pierwszy parametr jest wskaźnikiem na element listy, za którym ma być dodany nowy element. Drugi parametr zawiera, które mają być zapamiętane w nowym elemencie listy. Powyższa funkcja zwraca wartość 0 gdy operacja dodania elementu przebiega poprawnie oraz wartość 1 3

gdy element nie zostanie dodany z powodu braku pamięci na utworzenie zmiennej dynamicznej. Usuwanie początkowego elementu listy. Drugą ważną operacją wykonywaną na liście jest usuwanie elementu. Podobnie jak przy dodawaniu, trzeba wyróżnić dwa przypadki: usuwanie pierwszego elementu listy oraz usuwanie środkowego (ostatniego) elementu. Przypadek pierwszy ilustruje rysunek 4. a) b) Rys. 4 Ilustracja usuwania pierwszego elementu listy a) stan listy przed rozpoczęciem operacji, b) stan listy po zakończeniu operacji. Kolorem czerwonym zaznaczono usuwany węzeł oraz nowe dowiązanie, a numery na rysunku odpowiadają instrukcjom programu przedstawionego poniżej. void UsunPoczatek(Wezel *&) { Wezel *tmp; tmp = = tmp->nastepny; delete tmp; // free(tmp); Nagłówek funkcji (linia ) zawiera jeden parametr, który jest przekazanym przez referencję wskaźnikiem na początek listy. Do wykonania tej operacji konieczna jest pomocnicza zmienna wskaźnikowa tmp (deklaracja ), w której zostanie chwilowo zapamiętany adres usuwanego węzła (instrukcja ). W instrukcji modyfikowane jest dowiązanie pamiętane w zmiennej. Instrukcja usuwa zmienną dynamiczną, w której był pamiętany usuwany węzeł. Można to zrobić za pomocą operatora delete (tylko w języku C++) lub za pomocą funkcji free (zob. komentarz). Częstym błędem, przy usuwaniu elementu listy jest pomijanie instrukcji usuwania zmiennej dynamicznej. Jest to trudny do wykrycia błąd, który powoduje tzw. wycieki pamięci. Parametr musi być przekazany przez referencję, gdyż w wyniku wykonania tej funkcji adres początkowego elementu listy ulegnie zmianie. Usuwanie elementu listy znajdującego się za wskazanym elementem. Czasami zachodzi konieczność usuwania elementu w środku lub na końcu listy. Wówczas należy wskazać węzeł bezpośrednio poprzedzający usuwany węzeł. W poniższym przykładzie adres ten jest pamiętany w zmiennej poprzednik. Ilustruje to rysunek 5. 4

a) poprzednik b) poprzednik Rys. 5 Ilustracja usuwania środkowego elementu listy a) stan listy przed rozpoczęciem operacji, b) stan listy po zakończeniu operacji. Kolorem czerwonym zaznaczono usuwany węzeł oraz nowe dowiązanie, a numery na rysunku odpowiadają instrukcjom programu przedstawionego poniżej. void UsunNastępny(Wezel *poprzednik) { Wezel *tmp; tmp = poprzednik->nastepny; poprzednik->nastepny = tmp->nastepny; delete tmp; // free(tmp); Usuwanie wszystkich elementów listy. W niektórych sytuacjach zachodzi konieczność usunięcia wszystkich elementów listy. Wówczas można tego dokonać za pomocą prostej pętli, tak jak przedstawiono to w poniższym przykładzie: void UsunWszystkie(Wezel *&) { Wezel *tmp; while (!= ) { tmp = ; = tmp->nastepny; delete tmp; // free(tmp); 5

Przy usuwaniu wszystkich elementów nie wolno zapomnieć o usuwaniu zmiennych dynamicznych (instrukcja ), w których pamiętane są węzły listy. Pominięcie tej instrukcji jest trudnym do wykrycia błędem powodującym wycieki pamięci Parametr musi być przekazany przez referencję, gdyż w wyniku wykonania tej funkcji adres początkowego elementu listy ulegnie zmianie. Przechodzenie przez wszystkie elementy listy. Jedną z najczęściej wykonywanych operacji na listach jednokierunkowych jest przechodzenie przez wszystkie elementy listy od pierwszego do ostatniego. W trakcie takiego przechodzenia można wykonać na każdym elemencie jakąś operację. Typowym przykładem jest drukowanie na ekranie wszystkich danych zapamiętanych w liście. Ilustruje to poniższy przykład: void DrukujListe(Wezel *) { Wezel *tmp; for(tmp=; tmp!=; tmp=tmp->nastepny) { // tu nalezy wykonac operacje na elemencie // wskazywanym przez zmienną tmp. Np... printf( Adres elementu: %p\n, tmp) printf( Wartosc elementu: %20s %5.2f %d\n, tmp->.nazwa, tmp->.cena, tmp->.ilosc); Wyszukiwanie elementu spełniającego jakiś warunek. Często zachodzi konieczność wyszukania elementu listy, który spełnia określony warunek. Taki element może być później modyfikowany lub nawet usuwany. Wiele operacji, które mają być wykonane na wskazanym elemencie listy, mogą być wykonane tylko wówczas gdy znany jest element poprzedni. Przykładem takiej operacji jest usuwanie elementu, przedstawione w poprzednich punktach. Aby wykonanie takich operacji było możliwe należy pamiętać nie tylko adres bieżącego elementu, ale również adres poprzednika. Taki sposób wyszukiwania zastosowano w poniższej funkcji, która wyszukuje element listy zawierający towar o poj nazwie. int WyszukajTowar(Wezel *, char *nazwa, Wezel *&biezacy, *&poprzednik) { poprzednik = ; biezacy = ; while(biezacy!= ) { if ( strcmp(biezacy->.nazwa, nazwa)==0 ) { return 0; poprzednik = biezacy; biezacy = biezacy->nastepny; return 1; 6

W linii oznaczonej symbolem należy podać warunek który ma spełniać poszukiwany element listy. W tej funkcji jest porównywana nazwa towaru pamiętanego w bieżącym węźle listy z nazwą podaną w parametrze funkcji. Po wykonaniu powyższej funkcji w zmiennej biezacy będzie pamiętany adres elementu listy, który spełnia postawiony warunek. Jeśli zmienna biezacy ma wartość to w liście nie ma poszukiwanego elementu. Jeśli znaleziony element jest pierwszym elementem listy, to zmienna poprzednik ma wartość. W przeciwnym wypadku, zmienna ta zawiera adres elementu bezpośrednio poprzedzającego element biezacy. Funkcja zwraca wartość 0 jeśli element zostanie znaleziony lub wartość 1, gdy żaden z Węzłów listy nie zawiera towaru o poj nazwie. Parametry biezacy i poprzednik muszą być przekazane do funkcji przez referencję. Odwracanie kolejności elementów listy. Inną operacją wykonywaną na elementach listy może być odwracanie kolejności elementów listy. Taką operację wykonuje poniższe funkcja: void OdwracanieListy(Wezel *&) { Wezel * nowypoczatek = ; Wezel *tmp; while(!= ) { tmp = ->nastepny; ->nastepny = nowypoczatek; nowypoczatek = ; = tmp; = nowypoczatek; Odwracanie porządku polega na przepisaniu wszystkich elementów pierwotnej listy do pomocniczej listy, której początek jest pamiętany w zmiennej wskaźnikowej nowypoczatek. Po przepisaniu wszystkich elementów listy, parametrowi przypisywana jest wartość nowypoczatek. Parametr musi być przekazany do funkcji przez referencję. Sortowanie elementów listy. Sortowanie elementów listy jest jedną z bardziej złożonych operacji. Poważnym utrudnieniem jest brak możliwości bezpośredniego odwoływania się do elementów listy. Dlatego proces sortowania polega na budowaniu nowej listy z elementów listy pierwotnej (nieposortowanej). Sortowanie można realizować metodą selekcji lub wstawiania. W pierwszym przypadku z listy pierwotnej jest pobierany element największy i jest on wstawiany na początek listy sortowanej. Taka procedura jest powtarzana do momentu wyczerpania wszystkich elementów listy pierwotnej. Na zakończenie zmiennej wskazującej początek listy pierwotnej jest przypisywana wartość zmiennej wskazującej na początek listy posortowanej. Taki proces sortowania realizuje poniższa funkcja, która porządkuje elementy listy według ceny towarów pamiętanych w węzłach listy. 7

void SortujSelekcja(Wezel *&) { Wezel *nowypoczatek = ; Wezel *max, *popmax, *tmp; // max wskaźnik na element o maksymalnej cenie towaru // popmax wskaźnik na element poprzedzający max // lub, gdy max jest początkowym elementem while( ) { max = ; popmax = ; tmp = ; while(tmp->nastepny) { if (tmp->nastepny->.cena>max->.cena) { popmax = tmp; max = tmp->nastepny; tmp = tmp->nastepny; if (popmax) popmax->nastepny = max->nastepny; else = max->nastepny; max->nastepny = nowypoczatek; nowypoczatek = max; = nowypoczatek; 8