New Tree structure to test.
This commit is contained in:
264
src/main.cpp
264
src/main.cpp
@@ -30,6 +30,188 @@
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory> // Für std::unique_ptr
|
||||
#include <iterator> // Für std::forward_iterator_tag
|
||||
#include <algorithm> // Für std::find_if, std::next
|
||||
#include <cstddef> // Für std::ptrdiff_t
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @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 <typename T>
|
||||
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<std::unique_ptr<Tree<T>>> children;
|
||||
|
||||
// Nicht-besitzender Rohzeiger auf den Elternknoten für die Navigation nach oben.
|
||||
// Dieser ist für die Iterator-Implementierung entscheidend.
|
||||
Tree<T>* parent = nullptr;
|
||||
|
||||
// --- Konstruktoren ---
|
||||
explicit Tree(const T& val, Tree<T>* p = nullptr) : value(val), parent(p) {}
|
||||
explicit Tree(T&& val, Tree<T>* 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<T>* addChild(const T& val) {
|
||||
auto newNode = std::make_unique<Tree<T>>(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<T>* addChild(T&& val) {
|
||||
auto newNode = std::make_unique<Tree<T>>(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 <bool IsConst>
|
||||
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<T>' oder 'Tree<T>'
|
||||
using TreeT = std::conditional_t<IsConst, const Tree<T>, Tree<T>>;
|
||||
// value_type ist immer T (der Wert, nicht der Knoten)
|
||||
using value_type = T;
|
||||
using pointer = std::conditional_t<IsConst, const T*, T*>;
|
||||
using reference = std::conditional_t<IsConst, const T&, 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<false>;
|
||||
using const_iterator = BaseIterator<true>;
|
||||
|
||||
// --- 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<std::string> 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 <algorithm> (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 <numeric> (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;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user