From af3a7e06c28f2974f4b0a6cfa8f625a3660df1ba Mon Sep 17 00:00:00 2001 From: Christoph Holzheuer Date: Wed, 29 Oct 2025 13:43:43 +0100 Subject: [PATCH] New Tree structure to test. --- src/main.cpp | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index a53d187..cd9623d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,188 @@ using namespace Qt::Literals::StringLiterals; + +#include +#include +#include // Für std::unique_ptr +#include // Für std::forward_iterator_tag +#include // Für std::find_if, std::next +#include // Für std::ptrdiff_t +#include + +/** + * @brief Eine generische Baumstruktur, die als Knoten selbst fungiert. + * + * Diese Klasse implementiert einen Baum, bei dem jedes 'Tree'-Objekt + * einen Knoten im Baum darstellt. Sie verwendet std::unique_ptr + * für die Verwaltung der Kind-Knoten und ist durch Iteratoren + * mit STL-Algorithmen kompatibel (Pre-Order-Traversierung). + * + * @tparam T Der Typ des zu speichernden Wertes. + */ +template +class Tree +{ +public: + // --- Öffentliche Typ-Aliase --- + using value_type = T; + using reference = T&; + using const_reference = const T&; + + // --- Element-Zugriff --- + value_type value; + + // --- Struktur --- + // Wir verwenden unique_ptr, um das Eigentum klar zu definieren. + // Der Elternknoten besitzt seine Kinder. + std::vector>> children; + + // Nicht-besitzender Rohzeiger auf den Elternknoten für die Navigation nach oben. + // Dieser ist für die Iterator-Implementierung entscheidend. + Tree* parent = nullptr; + + // --- Konstruktoren --- + explicit Tree(const T& val, Tree* p = nullptr) : value(val), parent(p) {} + explicit Tree(T&& val, Tree* p = nullptr) : value(std::move(val)), parent(p) {} + + // Verhindere Kopieren, da unique_ptr nicht kopierbar ist (aber verschiebbar). + Tree(const Tree&) = delete; + Tree& operator=(const Tree&) = delete; + // Standard-Move-Konstruktor und -Zuweisung sind in Ordnung. + Tree(Tree&&) = default; + Tree& operator=(Tree&&) = default; + + // --- Methoden --- + + /** + * @brief Fügt ein neues Kind mit einem Wert hinzu (Kopie). + * @return Ein nicht-besitzender Zeiger auf den neu erstellten Knoten. + */ + Tree* addChild(const T& val) { + auto newNode = std::make_unique>(val, this); + children.push_back(std::move(newNode)); + return children.back().get(); + } + + /** + * @brief Fügt ein neues Kind mit einem Wert hinzu (Move). + * @return Ein nicht-besitzender Zeiger auf den neu erstellten Knoten. + */ + Tree* addChild(T&& val) { + auto newNode = std::make_unique>(std::move(val), this); + children.push_back(std::move(newNode)); + return children.back().get(); + } + +private: + // --- Private Basis-Iterator-Klasse (Template-Magie zur Vermeidung von Code-Duplizierung) --- + // Wir definieren einen Template-Iterator, der sowohl für const als auch für non-const funktioniert. + template + class BaseIterator { + public: + // Iterator-Traits (Eigenschaften) + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + + // Abhängige Typen: TreeT ist 'const Tree' oder 'Tree' + using TreeT = std::conditional_t, Tree>; + // value_type ist immer T (der Wert, nicht der Knoten) + using value_type = T; + using pointer = std::conditional_t; + using reference = std::conditional_t; + + private: + TreeT* current_node; + + public: + // Konstruktor + explicit BaseIterator(TreeT* node) : current_node(node) {} + + // Dereferenzierung + reference operator*() const { return current_node->value; } + pointer operator->() const { return &(current_node->value); } + + // Vergleichsoperatoren + bool operator==(const BaseIterator& other) const { + return current_node == other.current_node; + } + bool operator!=(const BaseIterator& other) const { + return current_node != other.current_node; + } + + // --- Inkrement (Kernlogik der Pre-Order-Traversierung) --- + // (Präfix-Version: ++it) + BaseIterator& operator++() { + if (!current_node) { + return *this; // Bereits am Ende + } + + // 1. Priorität: Gehe zum ersten Kind (Tiefensuche) + if (!current_node->children.empty()) { + current_node = current_node->children.front().get(); + return *this; + } + + // 2. Priorität: Gehe zum nächsten Geschwisterknoten oder gehe nach oben + while (current_node) { + // Wenn wir zur Wurzel zurückgekehrt sind und keine Geschwister haben, sind wir fertig. + if (!current_node->parent) { + current_node = nullptr; // Ende erreicht + return *this; + } + + TreeT* parent_node = current_node->parent; + + // Finde den aktuellen Knoten in der Kind-Liste des Elternteils + auto it = std::find_if(parent_node->children.begin(), parent_node->children.end(), + [this](const auto& child_ptr) { + return child_ptr.get() == current_node; + }); + + // Finde das nächste Geschwister + auto next_sibling_it = std::next(it); + + if (next_sibling_it != parent_node->children.end()) { + // 2a. Nächstes Geschwister gefunden + current_node = next_sibling_it->get(); + return *this; + } + + // 2b. Kein nächstes Geschwister, gehe eine Ebene nach oben + // Die Schleife wird mit dem parent_node wiederholt + current_node = parent_node; + } + + return *this; // Sollte jetzt nullptr sein + } + + // (Postfix-Version: it++) + BaseIterator operator++(int) { + BaseIterator temp = *this; + ++(*this); + return temp; + } + }; + +public: + + // --- Öffentliche Iterator-Typen --- + using iterator = BaseIterator; + using const_iterator = BaseIterator; + + // --- Iterator-Methoden (STL-Kompatibilität) --- + iterator begin() { return iterator(this); } + iterator end() { return iterator(nullptr); } + + const_iterator begin() const { return const_iterator(this); } + const_iterator end() const { return const_iterator(nullptr); } + + const_iterator cbegin() const { return const_iterator(this); } + const_iterator cend() const { return const_iterator(nullptr); } +}; + + +/* int main(int argc, char *argv[]) { @@ -45,9 +227,91 @@ int main(int argc, char *argv[]) return app.exec(); } +*/ +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + // hat am wenigsten darstellungfehler (alternative: fusion) + QQuickStyle::setStyle("Imagine"); + + + XQMainWindow window; + + // 1. Baum erstellen + // Der Benutzer erstellt den Wurzelknoten direkt. + Tree root("Root"); + + // Kinder hinzufügen. Die addChild-Methode gibt einen Zeiger + // auf den neuen Knoten zurück, sodass wir sie verketten können. + auto* a = root.addChild("A"); + auto* b = root.addChild("B"); + auto* c = root.addChild("C"); + + // Enkelkinder hinzufügen + a->addChild("A1"); + a->addChild("A2"); + + auto* b1 = b->addChild("B1 (suche mich)"); + + c->addChild("C1"); + c->addChild("C2"); + c->addChild("C3"); + + b1->addChild("B1.1"); + + /* + * Der Baum sieht nun so aus (Pre-Order): + * 1. Root + * 2. A + * 3. A1 + * 4. A2 + * 5. B + * 6. B1 (suche mich) + * 7. B1.1 + * 8. C + * 9. C1 + * 10. C2 + * 11. C3 + */ + + // 2. Verwendung mit Range-Based 'for' (dank begin/end) + std::cout << "--- Pre-Order Traversierung (Range-based for) ---" << std::endl; + for (const std::string& value : root) { + std::cout << value << " -> "; + } + std::cout << "end" << std::endl; + + + // 3. Verwendung mit (z.B. std::find_if) + std::cout << "\n--- STL std::find_if ---" << std::endl; + + // Finde den ersten Knoten, dessen Wert "B1 (suche mich)" ist. + // Wir verwenden cbegin/cend, da wir den Baum nicht ändern wollen. + auto found_it = std::find_if(root.cbegin(), root.cend(), [](const std::string& val) { + return val == "B1 (suche mich)"; + }); + + if (found_it != root.cend()) { + // *found_it gibt den Wert (std::string) zurück + std::cout << "Gefunden: " << *found_it << std::endl; + } else { + std::cout << "Nicht gefunden." << std::endl; + } + + // 4. Verwendung mit (z.B. std::count_if) + std::cout << "\n--- STL std::count_if ---" << std::endl; + int count = std::count_if(root.cbegin(), root.cend(), [](const std::string& val) { + return val.length() > 5; // Zähle alle Strings mit mehr als 5 Zeichen + }); + + std::cout << "Knoten mit > 5 Zeichen: " << count << std::endl; + + return 0; + +}