Najprostszymi dynamicznymi strukturami, które poznaliśmy, są stos i kolejka. Obydwie implementować można za pomocą różnych mechanizmów. Jedno z najprostszych rozwiązań, które służy zazwyczaj do wyjaśnienia działania tych struktur w języku Python, to użycie listy, jednej z wbudowanych struktur danych. Jest to struktura dynamiczna umożliwiająca zapisywanie dowolnej liczby elementów. Z jej pomocą można łatwo implementować operacje wykonywane na omawianych poniżej strukturach. Ponieważ jednak celem tego materiału jest wyjaśnienie zasad działania i implementowania struktur dynamicznych, wykorzystywane dalej listy skonstruujemy sami przy użyciu klas użytkownika.

Stos

Przypomnijmy, że stos to liniowa struktura typu LIFO (ang. Last‑In, First‑Out – ostatni na wejściu, pierwszy na wyjściu), która obsługuje dwie podstawowe operacje, tj. odkładanie i zdejmowanie danych. Dodatkowe działania, które wykonuje się na stosie, to odczytywanie wierzchołka stosu, czyli ostatniego odłożonego elementu, bez usuwania go, oraz ewentualnie sprawdzanie rozmiaru stosu, czyli liczby odłożonych elementów.

Ważne!

W programie korzystamy z biblioteki sys.

Biorąc powyższe warunki pod uwagę, do implementacji stosu wystarczy nam jednokierunkowa lista niecykliczna. Elementami tej listy będą węzły zawierające dane, np. liczby, oraz – w przypadku listy jednokierunkowej – referencjareferencjareferencja na element następny. Do zdefiniowania węzła użyjemy klasyklasaklasy zawierającej konstruktorkonstruktorkonstruktor inicjalizujący dwa pola klasy:

Linia 1. class Wezel dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 3. self kropka liczba znak równości liczba. Linia 4. self kropka nastepny znak równości None.

Stworzenie nowego węzła i przypisanie wartości do jego składowych umożliwia instrukcja:

Linia 1. nowy znak równości Wezel otwórz nawias okrągły liczba zamknij nawias okrągły.

Stos zaimplementujemy również jako klasę, której atrybutem inicjalizowanym w konstruktorze referencją pustąreferencjareferencją pustą będzie referencja na wierzchołek stosu o nazwie wierzcholek.

Linia 1. class Stos dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 3. self kropka wierzcholek znak równości None. Linia 5. kratka Poniżej dodamy kolejne metody.

Do klasy Stos() dodamy teraz metody wykonujące charakterystyczne dla stosu operacje.

Zaczniemy od metody czyPusty(), która będzie zwracała wartość true (prawda), jeżeli stos będzie pusty, czyli w sytuacji, kiedy pole wierzcholek będzie referencją pustą. W przeciwnym razie metoda zwróci wartość false (fałsz).

Linia 1. def czy podkreślnik pusty otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka wierzcholek is None dwukropek. Linia 3. print otwórz nawias okrągły cudzysłów Stos pusty wykrzyknik cudzysłów zamknij nawias okrągły. Linia 4. return True. Linia 5. return False.

W celu zachowania poprawności działania struktury LIFO, funkcja odloz() będzie dodawała elementy na początku listy i odpowiednio funkcja zdejmij() będzie usuwała elementy również z początku listy.

Zacznijmy od metody odloz():

Linia 1. def odloz otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 2. print otwórz nawias okrągły cudzysłów Odkladam cudzysłów przecinek liczba zamknij nawias okrągły. Linia 3. nowy znak równości Wezel otwórz nawias okrągły liczba zamknij nawias okrągły. Linia 4. nowy kropka nastepny znak równości self kropka wierzcholek. Linia 5. self kropka wierzcholek znak równości nowy.

W funkcji odloz() tworzymy nowy węzeł i zapisujemy w nim wartość zmiennej liczba przekazaną jako argument. Następnie pole nastepny nowego elementu ustawiamy na dotychczasowy wierzcholek, w ten sposób dodajemy element na początku listy. Na koniec aktualizujemy pole wierzcholek, tak aby wskazywało na nowo dodany element.

W metodzie pomijamy sprawdzenie ewentualnego błędu przepełnienia stosuprzepełnienie stosuprzepełnienia stosu, który ze względu na użycie listy jest mało prawdopodobny.

Usuwanie pierwszego elementu jest możliwe dzięki referencji wierzcholek:

Linia 1. def zdejmij otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusty otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return minus sys kropka maxsize minus 1. Linia 5. pomocniczy znak równości self kropka wierzcholek. Linia 6. liczba znak równości pomocniczy kropka liczba. Linia 7. self kropka wierzcholek znak równości self kropka wierzcholek kropka nastepny. Linia 9. print otwórz nawias okrągły cudzysłów Zdejmuje cudzysłów przecinek liczba zamknij nawias okrągły. Linia 10. return liczba.

Jeżeli stos jest pusty, tzn. zachodzi błąd niedopełnienia stosuniedopełnienie stosuniedopełnienia stosu, zwracamy minimalną wartość dla typu całkowitego, którą w języku wylicza się jako ujemną wartość maksymalną (-sys.maxsize) pomniejszoną o 1.

W przeciwnym razie dotychczasowy pierwszy element oraz zapisaną w nim wartość zapamiętujemy w zmiennych pomocniczych. Następnie ustawiamy wierzchołek na drugi element listy, dotychczasowy pierwszy usuwamy, a zapamiętaną wartość zwracamy.

Metoda pobierz_wierzcholek() ma tylko jedno zadanie: jeżeli stos nie jest pusty, zwraca wartość zapisaną w pierwszym, czyli ostatnim dodanym, elemencie listy:

Linia 1. def pobierz podkreślnik wierzcholek otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusty otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return minus sys kropka maxsize minus 1. Linia 4. return self kropka wierzcholek kropka liczba.

Metoda wypisz() posłuży do wypisania wartości odłożonych na stosie, czyli zapisanych w kolejnych węzłach listy:

Linia 1. def wypisz otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusty otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return. Linia 5. print otwórz nawias okrągły cudzysłów Stos zawiera dwukropek cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 6. pomocniczy znak równości self kropka wierzcholek. Linia 7. while pomocniczy dwukropek. Linia 8. print otwórz nawias okrągły pomocniczy kropka liczba przecinek cudzysłów cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 9. pomocniczy znak równości pomocniczy kropka nastepny średnik. Linia 10. print otwórz nawias okrągły zamknij nawias okrągły.

Do wypisania wartości używamy pętli przechodzącej po wszystkich elementach stosu zapisywanych w zmiennej pomocniczy, którą ustawiamy na wierzchołek, a następnie wewnątrz pętli przesuwamy na kolejne węzły wskazywane przez pole nastepny. Pętla będzie działać dopóki nie dojdziemy do ostatniego węzła, którego referencja nastepny będzie pusta.

Do sprawdzenia działania stosu możemy użyć poniższego kodu:

Linia 1. stos znak równości Stos otwórz nawias okrągły zamknij nawias okrągły. Linia 2. stos kropka odloz otwórz nawias okrągły 1 zamknij nawias okrągły. Linia 3. stos kropka odloz otwórz nawias okrągły 2 zamknij nawias okrągły. Linia 4. stos kropka odloz otwórz nawias okrągły 3 zamknij nawias okrągły. Linia 5. stos kropka wypisz otwórz nawias okrągły zamknij nawias okrągły. Linia 6. stos kropka zdejmij otwórz nawias okrągły zamknij nawias okrągły. Linia 7. stos kropka zdejmij otwórz nawias okrągły zamknij nawias okrągły. Linia 8. stos kropka zdejmij otwórz nawias okrągły zamknij nawias okrągły. Linia 9. stos kropka zdejmij otwórz nawias okrągły zamknij nawias okrągły.
Ćwiczenie 1

Na podstawie omówionego kodu przygotuj program ilustrujący działanie stosu. Przeanalizuj podaną funkcje główną i odpowiedz, jaki będzie ostatni wypisany przez program komunikat. Uruchom program i sprawdź poprawność swojej odpowiedzi.

RM4BEI8u73tF1

Kolejka

Kolejka to liniowa struktura danych typu FIFO (ang. First‑In, First‑Out – pierwszy na wejściu, pierwszy na wyjściu), co oznacza, że jej elementy odczytywane są zgodnie z kolejnością dodawania, a zatem odwrotnie niż w przypadku stosu.

W implementacji kolejki również użyjemy listy jednokierunkowej i takiej samej jak w przypadku stosu definicji klasy Wezel. Natomiast klasa Kolejka będzie zawierała dwa pola:

  • glowa – referencję na pierwszy element kolejki;

  • ogon – referencję na ostatni element kolejki, który ułatwi dodawanie elementów na końcu listy.

Nazwy pozostałych metod dostosujemy do omawianej struktury.

Linia 1. class Wezel dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 3. self kropka liczba znak równości liczba. Linia 4. self kropka nastepny znak równości None. Linia 6. class Kolejka dwukropek. Linia 7. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 8. self kropka glowa znak równości None. Linia 9. self kropka ogon znak równości None. Linia 11. kratka Poniżej dodamy kolejne metody.

Metoda czy_pusta() działa tak samo, jak w przypadku stosu i każdej listy, tzn. jeżeli referencja na pierwszy element jest pusta, lista jest pusta.

Linia 1. def czy podkreślnik pusta otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. return self kropka glowa is None.

Metoda dodaj() będzie dodawała nowe elementy na końcu listy przy użyciu pola ogon.

Linia 1. def dodaj otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 2. print otwórz nawias okrągły cudzysłów Dodaje dwukropek cudzysłów przecinek liczba zamknij nawias okrągły. Linia 3. nowy znak równości Wezel otwórz nawias okrągły liczba zamknij nawias okrągły. Linia 5. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 6. self kropka glowa znak równości self kropka ogon znak równości nowy. Linia 7. else dwukropek. Linia 8. self kropka ogon kropka nastepny znak równości nowy. Linia 9. self kropka ogon znak równości nowy.

Na początku metody tworzymy nowy węzeł, przypisujemy otrzymaną liczbę do jego pola liczba, a referencję nastepny ustawiamy na wartość pustą. Następnie, jeżeli lista jest pusta, referencje na pierwszy (glowa) i ostatni (ogon) element listy ustawiamy na nowy węzeł.

W przeciwnym razie referencję nastepny dotychczasowego ostatniego elementu ustawiamy na nowy węzeł i aktualizujemy referencję ogon, aby wskazywał nowo dodany element.

Metoda usun() będzie zwracała kolejne elementy z początku listy, czyli działać będzie tak samo, jak metoda zdejmij() w przypadku stosu. Ponieważ jednak elementy będą dodawane na końcu listy, zasada działania kolejki FIFO zostanie zachowana.

Linia 1. def usun otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return minus sys kropka maxsize minus 1. Linia 5. pomocniczy znak równości self kropka glowa. Linia 6. liczba znak równości pomocniczy kropka liczba. Linia 7. self kropka glowa znak równości self kropka glowa kropka nastepny. Linia 8. print otwórz nawias okrągły cudzysłów Usuwam dwukropek cudzysłów przecinek liczba zamknij nawias okrągły. Linia 9. return liczba.
Ćwiczenie 2

Wykorzystaj omówiony kod i przygotuj program pozwalający sprawdzić działanie kolejki. Dodaj do kolejki trzy liczby, wypisz kolejkę, a następnie wywołaj metodę usuwającą cztery razy.

R1LHghliGaqEJ

Lista

Przypomnijmy, że lista – podobnie jak stos i kolejka – jest liniową i dynamiczną strukturą danych. Elementami listy, jak już wiemy, są węzły, które zawierają dane oraz referencjęreferencjareferencję na element następny (w przypadku listy jednokierunkowej) lub na następny i poprzedni (w przypadku listy dwukierunkowej).

Do implementacji stosu i kolejki użyliśmy listy jednokierunkowej. Jej węzły zdefiniowaliśmy przy użyciu klasyklasaklasy zawierającej konstruktor argumentowy inicjalizujący dwa pola:

Linia 1. class Wezel dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 3. self kropka liczba znak równości liczba. Linia 4. self kropka nastepny znak równości None.

Konstruktor wymaga podania argumentu podczas tworzenia obiektu, co pozwala zainicjować pole liczba otrzymaną wartością:

Linia 1. wezel znak równości Wezel otwórz nawias okrągły 1 zamknij nawias okrągły.

Lista jednokierunkowa

Listę jednokierunkową implementujemy jako klasęklasaklasę z jednym polem glowa – referencją na pierwszy element listy. Oprócz tego w klasie definiujemy metody umożliwiające dodawanie, usuwanie i wypisywanie elementów listy. W konstruktorze __init__() inicjalizujemy pole glowa referencją pustąreferencjareferencją pustą.

Linia 1. class Lista dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 3. self kropka glowa znak równości None. Linia 5. kratka Poniżej dodamy kolejne metody.

Metody czy_pusta() oraz wypisz() działają tak samo jak w przypadku stosu i kolejki. Poniżej wyjaśnimy działanie metody dodającej węzły na końcu listy oraz usuwającej węzły o podanej pozycji.

Dodawanie węzłów na końcu listy jednokierunkowej

Metoda dodaj() jako argument będzie przyjmowała liczbę całkowitą. Nowy węzeł – inaczej niż w przypadku stosu i kolejki – będzie dodawany na końcu listy.

Linia 1. def dodaj otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 2. print otwórz nawias okrągły cudzysłów Dodaję dwukropek cudzysłów przecinek liczba zamknij nawias okrągły. Linia 4. kratka Tworzymy nowy węzeł. Linia 5. nowy znak równości Wezel otwórz nawias okrągły liczba zamknij nawias okrągły. Linia 7. kratka Jeżeli lista jest pusta. Linia 8. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 9. self kropka glowa znak równości nowy. Linia 10. return. Linia 12. kratka W przeciwnym razie przejdź do ostatniego elementu. Linia 13. pomocniczy znak równości self kropka glowa. Linia 14. while pomocniczy kropka nastepny wykrzyknik znak równości None dwukropek. Linia 15. pomocniczy znak równości pomocniczy kropka nastepny. Linia 17. kratka Dodajemy nowy węzeł na końcu listy. Linia 18. pomocniczy kropka nastepny znak równości nowy.

Na początku metody tworzymy nowy węzeł, przekazując do jego konstruktora otrzymaną liczbę. Następnie, jeżeli lista jest pusta, nowy węzeł staje się pierwszym elementem listy przypisanym do pola glowa.

W przeciwnym razie za pomocą węzła pomocniczy w pętli przechodzimy listę do ostatniego elementu, którego referencja nastepny będzie pusta. Po zakończeniu pętli pole nastepny dotychczasowego ostatniego elementu ustawiamy na nowy węzeł. W ten sposób dodajemy węzeł na końcu listy.

R1eSTXiWk4HIn
Schemat dodawania węzła do końca listy jednokierunkowej
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Usuwanie węzłów z listy jednokierunkowej

Metoda usuwająca węzły z listy otrzymywać będzie jako argument liczbę całkowitą wskazującą indeks czy też numer węzła, który chcemy usunąć. Przyjmijmy, że indeksy węzłów zaczynają się od 1.

Linia 1. def usun otwórz nawias okrągły self przecinek indeks zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return. Linia 5. print otwórz nawias okrągły cudzysłów Usuwam pozycje dwukropek cudzysłów przecinek indeks zamknij nawias okrągły. Linia 7. pomocniczy1 przecinek pomocniczy2 znak równości self kropka glowa przecinek None. Linia 8. rozmiar znak równości 0. Linia 10. kratka Zliczamy elementy listy. Linia 11. while pomocniczy1 wykrzyknik znak równości None dwukropek. Linia 12. pomocniczy1 znak równości pomocniczy1 kropka nastepny. Linia 13. rozmiar plus znak równości 1. Linia 15. kratka Jeżeli indeks jest większy od rozmiaru listy. Linia 16. if indeks zamknij nawias ostrokątny rozmiar dwukropek. Linia 17. print otwórz nawias okrągły cudzysłów Indeks poza zakresem kropka cudzysłów zamknij nawias okrągły. Linia 18. return. Linia 20. kratka Poniżej dodamy kod usuwający elementy.

Jeżeli lista nie jest pusta, definiujemy zmienne, których używamy w metodzie:

  • referencję pomocniczy1 ustawiamy na pierwszy element listy,

  • referencję pomocniczy2 wykorzystamy przy usuwaniu węzłów innych od pierwszego,

  • zmienna rozmiar posłuży do ustalenia rozmiaru listy.

Następnie zliczamy elementy listy za pomocą pętli while przechodzącej po wszystkich węzłach listy, zaczynając od pierwszego. Po dojściu do ostatniego elementu w zmiennej pomocniczy1 zostanie zapisana wartość None i pętla zakończy swoje działanie.

Ważne!

Warto zapamiętać omówioną przed chwilą pętlę, ponieważ przechodzenie wszystkich elementów listy (zaczynając od pierwszego) jest typową operacją wykonywaną na listach.

Następnie, jeżeli podany indeks nie jest większy od obliczonego rozmiaru listy, zaczynamy usuwanie węzła. Zadanie to realizuje kod, który umieszczamy na końcu omawianej metody:

Linia 1. kratka Usuwanie pierwszego węzła. Linia 2. pomocniczy1 znak równości self kropka glowa. Linia 3. if otwórz nawias okrągły indeks znak równości znak równości 1 zamknij nawias okrągły dwukropek. Linia 4. self kropka glowa znak równości self kropka glowa kropka nastepny. Linia 5. return. Linia 7. kratka Szukanie węzła do usunięcia otwórz nawias okrągły innego niż pierwszy zamknij nawias okrągły. Linia 8. while indeks zamknij nawias ostrokątny 1 dwukropek. Linia 9. pomocniczy2 znak równości pomocniczy1. Linia 10. pomocniczy1 znak równości pomocniczy1 kropka nastepny. Linia 11. indeks minus znak równości 1. Linia 13. kratka Zmiana referencji nastepny węzła poprzedzającego usuwany. Linia 14. pomocniczy2 kropka nastepny znak równości pomocniczy1 kropka nastepny.

Jeżeli usuwanym elementem jest pierwszy węzeł (indeks 1), pole glowa ustawiamy na następny węzeł, co powoduje wyłączenie dotychczasowego pierwszego elementu z listy.

W przeciwnym razie w pętli, która wykonuje się, dopóki indeks jest większy od 1, szukamy węzła do usunięcia. W każdej iteracji indeks jest zmniejszany o 1 dzięki notacji postfiksowej. Wewnątrz pętli przechodzimy po indeks - 1 elementach listy od jej początku, więc po jej zakończeniu zmienna pomocniczy1 będzie wskazywała węzeł do usunięcia, a zmienna pomocniczy2 węzeł poprzedni.

Po zakończeniu pętli w węźle poprzedzającym węzeł usuwany ustawiamy referencję nastepny na węzeł następny po węźle usuwanym (w przypadku usuwania ostatniego węzła będzie to wartość None).

Dla zainteresowanych

W językach programowania Python i Java nie trzeba usuwać węzła i zwalniać zajmowanej przez niego pamięci za pomocą osobnej instrukcji takiej jak np. wyrażenie delete() w języku C++. Jeżeli na jakiś obiekt nie wskazują żadne referencje, zostanie on usunięty przez mechanizm odśmiecania pamięci (ang. garbage collection).

R1DT71fUbiSfA
Schemat usuwania węzła z listy jednokierunkowej
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.
Ćwiczenie 3

Wykorzystaj omówiony kod i przygotuj program pozwalający sprawdzić działanie listy jednokierunkowej. Dodaj do listy trzy liczby i wypisz listę. Następnie usuń drugi element i wypisz listę. Na końcu jeszcze raz usuń drugi element, dodaj liczbę i wypisz listę.

R1JOgmCV4g9lN

Lista dwukierunkowa

Lista dwukierunkowa od jednokierunkowej różni się tym, że jej węzły, oprócz referencji na element następny, zawierają także referencję na element poprzedni. Implementację przygotujemy w oparciu o omówiony kod listy jednokierunkowej.

Na początku uzupełniamy klasę Wezel o pole poprzedni i uzupełniamy konstruktor tak, aby inicjalizował je wartością pustą. Do klasy Lista dodajemy pole ogon, które będzie wskazywało ostatni element listy. W konstruktorze klasy pola glowaogon ustawiamy na wartości puste.

Linia 1. class Wezel dwukropek. Linia 2. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 3. self kropka liczba znak równości liczba. Linia 4. self kropka nastepny znak równości None. Linia 5. self kropka poprzedni znak równości None. Linia 7. class Lista dwukropek. Linia 8. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 9. self kropka glowa znak równości self kropka ogon znak równości None. Linia 11. kratka Poniżej dodamy kolejne metody.

Pole ogon, tj. referencja na ostatni element listy, jest opcjonalne, może występować zarówno w listach jedno-, jak i dwukierunkowych. Używa się go m.in. do szybkiego dodawania elementów na końcu listy bez konieczności przechodzenia po wszystkich jej elementach w celu znalezienia ostatniego.

Dodawanie węzłów na końcu listy dwukierunkowej

Metody czy_pusta() oraz wypisz() pozostają bez zmian. Modyfikacji wymagają tylko implementacje metod dodaj()usun(). Zacznijmy od metody dodającej nowe węzły:

Linia 1. def dodaj otwórz nawias okrągły self przecinek liczba zamknij nawias okrągły dwukropek. Linia 2. print otwórz nawias okrągły cudzysłów Dodaję dwukropek cudzysłów przecinek liczba zamknij nawias okrągły. Linia 4. nowy znak równości Wezel otwórz nawias okrągły liczba zamknij nawias okrągły. Linia 6. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 7. self kropka glowa znak równości self kropka ogon znak równości nowy. Linia 8. return. Linia 10. self kropka ogon kropka nastepny znak równości nowy. Linia 11. nowy kropka poprzedni znak równości self kropka ogon. Linia 12. self kropka ogon znak równości nowy.

Różnice w porównaniu do implementacji tej metody w przypadku listy jednokierunkowej są następujące:

  • ustawienia pola ogon na nowy węzeł w przypadku dodawania pierwszego elementu listy,

  • wykorzystanie pola ogon – zamiast pętli przechodzącej wszystkie elementy listy – do wskazania ostatniego węzła listy,

  • ustawienie referencji nastepny dotychczasowego ostatniego węzła na nowy oraz poprzedni nowego węzła na dotychczasowy ostatni,

  • ustawienie pola ogon na nowo dodany element.

RlhFi1x6RJLwH
Schemat dodawania węzła do końca listy dwukierunkowej
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Usuwanie węzłów z listy dwukierunkowej

Metoda usuwająca elementy z podanej pozycji nie wymaga zmian, lecz uzupełnienia. Jeżeli usuwamy element niebędący ani pierwszym, ani ostatnim, musimy odpowiednio ustawić referencję poprzedni węzła występującego po węźle usuwanym. W przeciwnym razie, jeżeli usuwamy ostatni element, musimy zaktualizować pole ogon, aby wskazywało na dotychczasowy węzeł przedostatni. Poniżej kod uzupełnionej metody usun(). W komentarzu zaznaczyliśmy instrukcję warunkową realizującą omówione operacje:

Linia 1. def usun otwórz nawias okrągły self przecinek indeks zamknij nawias okrągły dwukropek. Linia 2. if self kropka czy podkreślnik pusta otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 3. return. Linia 5. print otwórz nawias okrągły cudzysłów Usuwam pozycje dwukropek cudzysłów przecinek indeks zamknij nawias okrągły. Linia 7. pomocniczy1 przecinek pomocniczy2 znak równości self kropka glowa przecinek None. Linia 8. rozmiar znak równości 0. Linia 10. while pomocniczy1 wykrzyknik znak równości None dwukropek. Linia 11. pomocniczy1 znak równości pomocniczy1 kropka nastepny. Linia 12. rozmiar plus znak równości 1. Linia 14. if indeks zamknij nawias ostrokątny rozmiar dwukropek. Linia 15. print otwórz nawias okrągły cudzysłów Indeks poza zakresem kropka cudzysłów zamknij nawias okrągły. Linia 16. return. Linia 18. if otwórz nawias okrągły indeks znak równości znak równości 1 zamknij nawias okrągły dwukropek. Linia 19. self kropka glowa znak równości self kropka glowa kropka nastepny. Linia 20. return. Linia 22. pomocniczy1 znak równości self kropka glowa. Linia 23. while indeks zamknij nawias ostrokątny 1 dwukropek. Linia 24. pomocniczy2 znak równości pomocniczy1. Linia 25. pomocniczy1 znak równości pomocniczy1 kropka nastepny. Linia 26. indeks minus znak równości 1. Linia 28. pomocniczy2 kropka nastepny znak równości pomocniczy1 kropka nastepny. Linia 30. kratka Kod obsługujący listę dwukierunkową. Linia 31. if pomocniczy1 kropka nastepny wykrzyknik znak równości None dwukropek. Linia 32. kratka Jeżeli usuwany węzeł nie jest ostatni. Linia 33. pomocniczy1 kropka nastepny kropka poprzedni znak równości pomocniczy2. Linia 34. else dwukropek. Linia 35. kratka Jeżeli usuwany element jest ostatni. Linia 36. self kropka ogon znak równości pomocniczy2.
R50oTO05RitRz
Schemat usuwania węzła z listy dwukierunkowej
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.
Ćwiczenie 4

Wykorzystaj omówiony kod i przygotuj program pozwalający sprawdzić działanie listy dwukierunkowej. Dodaj do listy trzy liczby i wypisz listę. Następnie usuń pierwszy, drugi i trzeci element. Dodaj do listy jeszcze jedną liczbę i wypisz listę.

R1IVEReZedSWn

Klasa deque z modułu collections

Dla zainteresowanych

Jak zaznaczyliśmy na początku materiału, jedną z podstawowych struktur danych wbudowanych w język Python jest lista. Jest to struktura dynamiczna ogólnego zastosowania i może służyć do implementacji stosu czy kolejki, jednak pamiętać należy, że listy w Pythonie optymalizowane są dla struktur o stałych rozmiarach.

Jeżeli więc zmierzamy korzystać ze struktur, w których liczba elementów często się zmienia, użyć należy klasy deque z modułu collections. Nazwa klasy jest skrótem od angielskich słów double‑ended queue, co oznacza kolejkę dwukierunkową. Klasa implementuje wyspecjalizowany kontener oferujący zoptymalizowane operacje dodawania i usuwania elementów z początku i końca struktury.

Klasa deque udostępnia m. in. następujące metody:

  • append() – zadaniem metody jest dodawanie elementu na końcu listy;

  • pop() – metoda zwraca ostatni element listy i go usuwa;

  • appendleft() – zadaniem metody jest dodawanie elementu na początku listy;

  • popleft() – metoda zwraca pierwszy element listy i go usuwa.

Pozostałe funkcjonalności danej struktury (oznaczonej poniżej nazwą s) uzyskujemy za pomocą standardowych mechanizmów języka. Możemy używać następujących instrukcji:

  • len(s) – instrukcja zwraca liczbę elementów listy;

  • s[0] – zadaniem instrukcji jest zwrócenie bez usuwania pierwszego elementu listy;

  • s[-1] lub s[len(s) - 1] – zadaniem instrukcji jest zwrócenie bez usuwania ostatniego elementu listy;

  • if s:, while s:, return s – instrukcja sprawdza, czy lista jest pusta; jeśli tak, s przyjmie wartość False, w przeciwnym razie True.

Kontener deque jako stos

Ważne!

Elementy stosu dodawane będą na końcu struktury i odczytywane będą również od końca, w ten sposób zachowana zostanie zasada LIFO.

Przykład 1

Przykładowy program:

Linia 1. from collections import deque. Linia 3. stos znak równości deque otwórz nawias okrągły zamknij nawias okrągły. Linia 5. print otwórz nawias okrągły cudzysłów Odkladam liczby 5 przecinek 4 przecinek 6 cudzysłów zamknij nawias okrągły. Linia 6. stos kropka append otwórz nawias okrągły 5 zamknij nawias okrągły średnik. Linia 7. stos kropka append otwórz nawias okrągły 4 zamknij nawias okrągły średnik. Linia 8. stos kropka append otwórz nawias okrągły 6 zamknij nawias okrągły średnik. Linia 9. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły stos zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 10. print otwórz nawias okrągły cudzysłów Wierzcholek dwukropek cudzysłów przecinek stos otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 12. print otwórz nawias okrągły cudzysłów Zdejmuje wierzcholek dwukropek cudzysłów przecinek stos kropka pop otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 13. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły stos zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 14. print otwórz nawias okrągły cudzysłów Wierzcholek dwukropek cudzysłów przecinek stos otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 16. print otwórz nawias okrągły cudzysłów Stos zawiera dwukropek cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 17. while stos dwukropek. Linia 18. print otwórz nawias okrągły stos kropka pop otwórz nawias okrągły zamknij nawias okrągły przecinek cudzysłów cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 20. print otwórz nawias okrągły zamknij nawias okrągły. Linia 21. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły stos zamknij nawias okrągły zamknij nawias okrągły.

Na początku tworzymy obiekt stos i odkładamy trzy liczby za pomocą metody append(), która dodaje je na końcu. Następnie wypisujemy rozmiar (len(stos)) i wierzchołek (stos[-1]) stosu.

W kolejnych instrukcjach zdejmujemy wierzchołek ze stosu (pop()), wypisujemy rozmiar i nowy wierzchołek.

W pętli (while stos:) wykonującej się, dopóki stos nie jest pusty, zdejmujemy i wypisujemy kolejne wierzchołki. Na końcu wypisujemy jeszcze raz aktualny rozmiar stosu.

Wynik działania programu:

Linia 1. Odkladam liczby 5 przecinek 4 przecinek 6. Linia 2. Rozmiar dwukropek 3 Wierzcholek dwukropek 6. Linia 3. Zdejmuje wierzcholek dwukropek 6. Linia 4. Rozmiar dwukropek 2 Wierzcholek dwukropek 4. Linia 5. Stos zawiera dwukropek 4 5. Linia 6. Rozmiar dwukropek 0.

Kontener deque jako kolejka

Ważne!

Elementy kolejki dodawane będą na końcu struktury, ale odczytywane będą od początku, w ten sposób zachowana zostanie zasada FIFO.

Przykład 2

Przykładowy program:

Linia 1. from collections import deque. Linia 3. kolejka znak równości deque otwórz nawias okrągły zamknij nawias okrągły. Linia 5. print otwórz nawias okrągły cudzysłów Dodaje liczby 5 przecinek 4 przecinek 6 cudzysłów zamknij nawias okrągły. Linia 6. kolejka kropka append otwórz nawias okrągły 5 zamknij nawias okrągły średnik. Linia 7. kolejka kropka append otwórz nawias okrągły 4 zamknij nawias okrągły średnik. Linia 8. kolejka kropka append otwórz nawias okrągły 6 zamknij nawias okrągły średnik. Linia 9. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły kolejka zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 10. print otwórz nawias okrągły cudzysłów Pierwszy dwukropek cudzysłów przecinek kolejka otwórz nawias kwadratowy 0 zamknij nawias kwadratowy przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 11. print otwórz nawias okrągły cudzysłów Ostatni dwukropek cudzysłów przecinek kolejka otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 13. print otwórz nawias okrągły cudzysłów Usuwam pierwszy dwukropek cudzysłów przecinek kolejka kropka popleft otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 14. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły kolejka zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 15. print otwórz nawias okrągły cudzysłów Pierwszy dwukropek cudzysłów przecinek kolejka otwórz nawias kwadratowy 0 zamknij nawias kwadratowy przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 16. print otwórz nawias okrągły cudzysłów Ostatni dwukropek cudzysłów przecinek kolejka otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 18. print otwórz nawias okrągły cudzysłów Lista zawiera dwukropek cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 19. while kolejka dwukropek. Linia 20. print otwórz nawias okrągły kolejka kropka popleft otwórz nawias okrągły zamknij nawias okrągły przecinek cudzysłów cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 22. print otwórz nawias okrągły zamknij nawias okrągły. Linia 23. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły kolejka zamknij nawias okrągły zamknij nawias okrągły.

Na początku tworzymy obiekt kolejka i dodajemy trzy liczby (append()), po czym wypisujemy rozmiar (len(kolejka)), pierwszy (kolejka[0]) i ostatni (kolejka[-1]) element.

Następnie usuwamy pierwszy element (popleft()) i ponownie wypisujemy rozmiar, pierwszy i ostatni element.

W pętli (while kolejka:) wykonującej się, dopóki kolejka nie jest pusta, usuwamy i wypisujemy kolejne pierwsze elementy. Na końcu wypisujemy jeszcze raz aktualny rozmiar kolejki.

Wynik działania programu:

Linia 1. Dodaje liczby 5 przecinek 4 przecinek 6. Linia 2. Rozmiar dwukropek 3 Pierwszy dwukropek 5 Ostatni dwukropek 6. Linia 3. Usuwam pierwszy dwukropek 5. Linia 4. Rozmiar dwukropek 2 Pierwszy dwukropek 4 Ostatni dwukropek 6. Linia 5. Lista zawiera dwukropek 4 6. Linia 6. Rozmiar dwukropek 0.

Kontener deque jako lista

Przykład 3

Przykładowy program:

Linia 1. from collections import deque. Linia 3. lista znak równości deque otwórz nawias okrągły zamknij nawias okrągły. Linia 5. print otwórz nawias okrągły cudzysłów Dodaje liczby 5 przecinek 4 na koncu cudzysłów zamknij nawias okrągły. Linia 6. lista kropka append otwórz nawias okrągły 5 zamknij nawias okrągły średnik. Linia 7. lista kropka append otwórz nawias okrągły 4 zamknij nawias okrągły średnik. Linia 8. print otwórz nawias okrągły cudzysłów Dodaje liczbe 6 na poczatku cudzysłów zamknij nawias okrągły. Linia 9. lista kropka appendleft otwórz nawias okrągły 6 zamknij nawias okrągły. Linia 10. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły lista zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 11. print otwórz nawias okrągły cudzysłów Pierwszy dwukropek cudzysłów przecinek lista otwórz nawias kwadratowy 0 zamknij nawias kwadratowy przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 12. print otwórz nawias okrągły cudzysłów Ostatni dwukropek cudzysłów przecinek lista otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 14. print otwórz nawias okrągły cudzysłów Usuwam ostatni dwukropek cudzysłów przecinek lista kropka pop otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 15. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły lista zamknij nawias okrągły przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 16. print otwórz nawias okrągły cudzysłów Pierwszy dwukropek cudzysłów przecinek lista otwórz nawias kwadratowy 0 zamknij nawias kwadratowy przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 17. print otwórz nawias okrągły cudzysłów Ostatni dwukropek cudzysłów przecinek lista otwórz nawias kwadratowy minus 1 zamknij nawias kwadratowy zamknij nawias okrągły. Linia 19. print otwórz nawias okrągły cudzysłów Lista zawiera dwukropek cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 20. while lista dwukropek. Linia 21. print otwórz nawias okrągły lista kropka popleft otwórz nawias okrągły zamknij nawias okrągły przecinek cudzysłów cudzysłów przecinek end znak równości cudzysłów cudzysłów zamknij nawias okrągły. Linia 23. print otwórz nawias okrągły zamknij nawias okrągły. Linia 24. print otwórz nawias okrągły cudzysłów Rozmiar dwukropek cudzysłów przecinek len otwórz nawias okrągły lista zamknij nawias okrągły zamknij nawias okrągły.

Na początku tworzymy obiekt lista i dodajemy dwie liczby (append()), następnie jedną na początku (appendleft()), po czym wypisujemy rozmiar (len(lista)) oraz pierwszy (lista[0]) i ostatni (lista[-1]) element.

Następnie usuwamy ostatni element (pop()) oraz ponownie wypisujemy rozmiar, pierwszy i ostatni element listy.

W pętli (while lista:) wykonującej się, dopóki lista nie jest pusta, usuwamy i wypisujemy kolejne pierwsze elementy (popleft()). Na końcu wypisujemy jeszcze raz aktualny rozmiar listy.

Wynik działania programu:

Linia 1. Dodaje liczby 5 przecinek 4 na koncu. Linia 2. Dodaje liczbe 6 na poczatku. Linia 3. Rozmiar dwukropek 3 Pierwszy dwukropek 6 Ostatni dwukropek 4. Linia 4. Usuwam ostatni dwukropek 4. Linia 5. Rozmiar dwukropek 2 Pierwszy dwukropek 6 Ostatni dwukropek 5. Linia 6. Lista zawiera dwukropek 6 5. Linia 7. Rozmiar dwukropek 0.

Słownik

klasa
klasa

definiowany przez użytkownika złożony typ danych, który pozwala grupować logicznie powiazane dane oraz funkcje wykonujące na nich operacje; składowe klasy nazywamy polami, funkcje zdefiniowane w klasie nazywa się metodami

konstruktor
konstruktor

metoda o takie samej nazwie jak klasa, która służy do nadawania wartości początkowych jej polom; w języku C++ klasa może mieć wiele konstruktorów dzięki mechanizmowi przeciążania

niedopełnienie stosu
niedopełnienie stosu

(ang. stack underflow) – rodzaj błędu występującego kiedy program próbuje zdjąć wartość z pustego stosu.

przepełnienie stosu
przepełnienie stosu

(ang. stack overflow) – rodzaj błędu polegającego na przekroczeniu rozmiaru pamięci zarezerwowanej dla stosu.

referencja
referencja

zmienna zawierająca informacje o położeniu jakiejś wartości (obiektu) w pamięci; referencje zarządzane są przez kompilator lub interpreter, a nie przez programistę, dzięki czemu są bezpieczniejsze niż wskaźniki; referencje mogą być puste, co znaczy, że nie wskazują na żaden obiekt, w języku Java wyraża to wartość null, w języku Python None

wskaźnik
wskaźnik

zmienna przechowująca adres innej zmiennej lub obiektu, pozwalająca na bezpośredni dostęp do pamięci; możliwe jest tworzenie wskaźników pustych, które na nic nie wskazują