Úloha 3.2

Napíšte program, zdrojový kód, v jazyku C++ použitím štandardu C++17, ktorý realizuje nasledovnú činnosť.

Pre implementovanie tejto úlohy môžte pokračovať s vypracovaním predchádzajúcej úlohy 3.1.

Implementujte obojstranne zreťazený zoznam. Vypracujte vlastnú implementáciu zreťazeného zoznamu, ktorého prvok je hodnotu typu string. Prvok zoznamu obsahuje pointer na ďalší prvok zoznamu a zároveň aj prvok na predchádzajúci prvok zoznamu. Pointer na ďalší prvok má hodnotu NULL ak ide o posledný prvok zoznamu. Pointer na predchádzajúci prvok má hodnotu NULL ak ide o prvý prvok zoznamu. Zoznam implementujte bez použitia STL knižníc ( array, list, vector a ďalšie).

Pre implementácia zoznamu použitie triedy.

Pre zoznam implementujte nasledovné funkcie:

  • string begin() - Vráti string hodnotu prvého prvku zoznamu. Ak je zoznam prázdny vráti prázdny string.
  • string end() - Vráti string hodnotu posledného prvku zoznamu. Ak je zoznam prázdny vráti prázdny string.
  • string at(int index) - Vráti string hodnotu prvku na definovanom indexe v zozname. Zoznam je indexovaný od 0. Ak index v zozname neexistuje vráti prázdny string.
  • void insert(int index, string value) - Vloží novú string hodnotu na definovaný index v zozname. Ak je index väčší ako veľkosť zoznamu vloží nový prvok na koniec zoznamu.
  • void remove(int index) - Vymaže prvok z definovaného indexu v zozname. Ak index v zozname neexistuje funkcia neurobí nič.
  • string to_string() - Vráti string reprezentáciu všetkých prvkov zoznamu. Hodnoty prvkov zoznamu sú uvedené v poradí v akom sú uložené a sú oddelené čiarkou. Ak je zoznam prázdny vráti prázdny string.

Funkcie môžte implementovať samostatne (vtedy ešte pridajte argument pre zoznam samotný), alebo ako členy triedy ( odporúčaný spôsob).

Pre demonštráciu vypracovania vytvorte ľubovolný zoznam, do ktorého postupne pridáte viacero prvkov, zavoláte na ňom funkcie begin() a end() a potom funkcie remove() a insert() v ľubovolnom poradí. Po každej úprave zoznamu vypíšte na štandardný výstup, výstup funkcie to_string() pre vizuálne znázornenie funkcionality.

Príklady volania funkcií

V nasledujúcom príklade je zoznam uložený do premennej zoznam:

zoznam.insert(99,"Milan");
zoznam.insert(99,"Jano");
zoznam.insert(99,"Fero");
cout << zoznam.to_string() << endl; // Milan, Jano, Fero

cout << zoznam.begin() << endl; // Milan
cout << zoznam.end() << endl; // Fero
cout << zoznam.at(1) << endl; // Jano

zoznam.remove(1);
cout << zoznam.to_string() << endl; // Milan, Fero
zoznam.insert(1,"Mária");
cout << zoznam.to_string() << endl; // Milan, Mária, Fero

Rozbaľ pre ukážku riešenia
#include <iostream>
#include <string>

using namespace std;

// Trieda reprezentujúca uzol v obojstranne zreťazenom zozname
class Node {
public:
    string value; // Hodnota uzla
    Node* next; // Ukazovateľ na ďalší uzol
    Node* prev; // Ukazovateľ na predchádzajúci uzol

    // Konštruktor inicializuje hodnotu a nastaví ukazovatele na nullptr
    Node(string val) : value(val), next(nullptr), prev(nullptr) {}
};

// Trieda reprezentujúca obojstranne zreťazený zoznam
class DoublyLinkedList {
private:
    Node* head; // Ukazovateľ na prvý uzol zoznamu
    Node* tail; // Ukazovateľ na posledný uzol zoznamu

public:
    DoublyLinkedList() : head(nullptr), tail(nullptr) {} // Konštruktor inicializuje prázdny zoznam

    // Dekštruktor, ktorý uvoľní všetky uzly zoznamu
    ~DoublyLinkedList() {
        Node* current = head;
        while (current) {
            Node* temp = current;
            current = current->next;
            delete temp;
        }
    }

    // Funkcia vráti hodnotu prvého uzla, ak existuje, inak vráti prázdny reťazec
    string begin() {
        return head ? head->value : "";
    }

    // Funkcia vráti hodnotu posledného uzla, ak existuje, inak vráti prázdny reťazec
    string end() {
        return tail ? tail->value : "";
    }

    // Funkcia vráti hodnotu uzla na danom indexe, ak existuje, inak vráti prázdny reťazec
    string at(int index) {
        Node* current = head;
        int count = 0;
        while (current) {
            if (count == index) return current->value;
            current = current->next;
            count++;
        }
        return "";
    }

    // Vloží nový uzol na daný index. Ak index neexistuje, pridá uzol na koniec.
    void insert(int index, string value) {
        Node* newNode = new Node(value);
        if (!head) { // Ak je zoznam prázdny, nový uzol sa stáva hlavou aj chvostom
            head = tail = newNode;
            return;
        }
        
        if (index <= 0) { // Vloženie na začiatok
            newNode->next = head;
            head->prev = newNode;
            head = newNode;
            return;
        }
        
        Node* current = head;
        int count = 0;
        while (current->next && count < index - 1) { // Posun na správnu pozíciu
            current = current->next;
            count++;
        }
        newNode->next = current->next;
        newNode->prev = current;
        if (current->next) {
            current->next->prev = newNode;
        } else {
            tail = newNode;
        }
        current->next = newNode;
    }

    // Odstráni uzol na danom indexe, ak existuje
    void remove(int index) {
        if (!head) return;
        if (index == 0) { // Odstránenie prvého uzla
            Node* temp = head;
            head = head->next;
            if (head) head->prev = nullptr;
            else tail = nullptr;
            delete temp;
            return;
        }
        
        Node* current = head;
        int count = 0;
        while (current->next && count < index - 1) { // Posun na uzol pred tým, ktorý chceme odstrániť
            current = current->next;
            count++;
        }
        if (current->next) { // Ak existuje uzol na odstránenie
            Node* temp = current->next;
            current->next = temp->next;
            if (temp->next) temp->next->prev = current;
            else tail = current;
            delete temp;
        }
    }

    // Funkcia vráti reťazcovú reprezentáciu zoznamu oddelenú čiarkami
    string to_string() {
        if (!head) return "";
        string result = "";
        Node* current = head;
        while (current) {
            result += current->value;
            if (current->next) result += ", ";
            current = current->next;
        }
        return result;
    }
};

int main() {
    DoublyLinkedList zoznam;

    // Vkladanie prvkov na koniec zoznamu
    zoznam.insert(99, "Milan");
    zoznam.insert(99, "Jano");
    zoznam.insert(99, "Fero");
    cout << zoznam.to_string() << endl; // Milan, Jano, Fero

    // Výpis prvého a posledného prvku
    cout << "First: " << zoznam.begin() << endl; // Milan
    cout << "Last: " << zoznam.end() << endl; // Fero
    cout << "At index 1: " << zoznam.at(1) << endl; // Jano

    // Odstránenie prvku na indexe 1
    zoznam.remove(1);
    cout << zoznam.to_string() << endl; // Milan, Fero
    
    // Vloženie nového prvku na index 1
    zoznam.insert(1, "Mária");
    cout << zoznam.to_string() << endl; // Milan, Mária, Fero

    return 0;
}