added ws202425 courses
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
# Übung 9
|
||||
|
||||
## 2. Abstrakte Datentypen
|
||||
|
||||
### (a) Linked List
|
||||
|
||||
Eine Queue kann mit Hilfe einer verketteten Liste implementiert werden, indem wir zwei Zeiger verwenden, die auf das vorderste (Front) und das hinterste Element (Rear) der Liste zeigen. Die Knoten der verketteten Liste müssen einen Zeiger auf den nächsten Knoten in der Liste sowie den eigentlichen Wert speichern.
|
||||
|
||||
```
|
||||
is_empty():
|
||||
Überprüfe, ob der Front Zeiger auf null zeigt. Wenn ja, ist die Queue leer und die Methode gibt true zurück. Andernfalls gibt die Methode false zurück.
|
||||
|
||||
dequeue():
|
||||
// Überprüfe zuerst, ob die Queue leer ist.
|
||||
if is_empty():
|
||||
// Wenn ja, werfe eine Exception oder gebe eine Fehlermeldung aus.
|
||||
raise Exception("Queue is empty")
|
||||
else:
|
||||
// Wenn die Queue nicht leer ist, speichere den Wert des Front Knotens.
|
||||
temp = front.value
|
||||
// Verschiebe den Front Zeiger auf den nächsten Knoten in der Liste.
|
||||
front = front.next
|
||||
// Wenn Front jetzt null ist (die Queue ist leer), setze Rear auch auf null.
|
||||
if front is null:
|
||||
rear = null
|
||||
// Gib den gespeicherten Wert zurück.
|
||||
return temp
|
||||
|
||||
front():
|
||||
Überprüfe, ob die Queue leer ist. Wenn ja, werfe eine Exception oder gebe eine Fehlermeldung aus.
|
||||
Wenn die Queue nicht leer ist, gebe einfach den Wert des Front Knotens zurück.
|
||||
|
||||
enqueue(x):
|
||||
// Erstelle einen neuen Knoten mit dem Wert x.
|
||||
newNode = new Node(x)
|
||||
|
||||
// Überprüfe, ob die Queue leer ist.
|
||||
if is_empty():
|
||||
// Wenn die Queue leer ist, setze Front und Rear auf den neuen Knoten.
|
||||
front = newNode
|
||||
rear = newNode
|
||||
else:
|
||||
// Wenn die Queue nicht leer ist, hänge den neuen Knoten an das Ende der Liste an.
|
||||
rear.next = newNode
|
||||
// Aktualisiere den Rear Zeiger auf den neuen Knoten.
|
||||
rear = newNode
|
||||
```
|
||||
|
||||
### (b) Dynamisches Array
|
||||
|
||||
Das Konzept des Wrap-Arounds bedeutet, dass das Ende der Warteschlange mit dem Anfang des Arrays verbunden wird, um den begrenzten Speicherplatz optimal zu nutzen.
|
||||
|
||||
```
|
||||
enqueue(x):
|
||||
// Überprüfe, ob die Queue voll ist. Dies ist der Fall, wenn das nächste Element nach "rear" der "front"-Index ist.
|
||||
if (rear + 1) % size == front:
|
||||
// Wenn die Queue voll ist, erweitere das Array oder gebe eine Fehlermeldung aus.
|
||||
raise Exception("Queue is full")
|
||||
else:
|
||||
// Wenn die Queue nicht voll ist, füge das Element an der "rear"-Position ein.
|
||||
array[rear] = x
|
||||
// Aktualisiere den "rear"-Index mit einem Wrap-Around.
|
||||
rear = (rear + 1) % size
|
||||
|
||||
dequeue():
|
||||
// Überprüfe zuerst, ob die Queue leer ist. Dies ist der Fall, wenn "front" und "rear" gleich sind.
|
||||
if front == rear:
|
||||
// Wenn ja, werfe eine Exception oder gebe eine Fehlermeldung aus.
|
||||
raise Exception("Queue is empty")
|
||||
else:
|
||||
// Wenn die Queue nicht leer ist, speichere den Wert an der "front"-Position.
|
||||
temp = array[front]
|
||||
// Aktualisiere den "front"-Index mit einem Wrap-Around.
|
||||
front = (front + 1) % size
|
||||
// Gib den gespeicherten Wert zurück.
|
||||
return temp
|
||||
|
||||
front():
|
||||
// Überprüfe, ob die Queue leer ist.
|
||||
if front == rear:
|
||||
// Wenn ja, werfe eine Exception oder gebe eine Fehlermeldung aus.
|
||||
raise Exception("Queue is empty")
|
||||
else:
|
||||
// Wenn die Queue nicht leer ist, gebe einfach den Wert an der "front"-Position zurück.
|
||||
return array[front]
|
||||
|
||||
is_empty():
|
||||
// Überprüfe, ob der "front"- und "rear"-Index gleich sind. Wenn ja, ist die Queue leer und die Methode gibt true zurück.
|
||||
return front == rear
|
||||
```
|
||||
|
||||
## 3. ADT Wörterbuch
|
||||
|
||||
### (b)
|
||||
|
||||
#### (i)
|
||||
|
||||
```
|
||||
Funktion Bereichsabfrage(Array, a, b):
|
||||
IndexA = binäreSuche(Array, a)
|
||||
IndexB = binäreSuche(Array, b)
|
||||
|
||||
wenn IndexA ist nicht gefunden oder IndexB ist nicht gefunden:
|
||||
Rückgabe 'Elemente nicht gefunden'
|
||||
|
||||
für i = IndexA bis IndexB:
|
||||
print Array[i]
|
||||
```
|
||||
|
||||
#### (ii)
|
||||
|
||||
```
|
||||
Beginnen Sie bei der Wurzel des Baums.
|
||||
Wenn der Schlüssel des aktuellen Knotens kleiner als a ist, suchen Sie im rechten Unterbaum.
|
||||
Wenn der Schlüssel des aktuellen Knotens größer als b ist, suchen Sie im linken Unterbaum.
|
||||
Wenn der Schlüssel des aktuellen Knotens zwischen a und b liegt, dann fügen Sie den Knoten in die Ergebnisliste ein und suchen Sie sowohl im linken als auch im rechten Unterbaum.
|
||||
```
|
||||
|
||||
Diese Suche kann rekursiv durchgeführt werden und gibt alle Elemente zurück, die zwischen a und b liegen.
|
||||
|
||||
## 4. Move
|
||||
|
||||
### (a)
|
||||
|
||||
Der Move-Konstruktor und der Move-Assignment Operator sind Funktionen in C++, die Teil des Konzepts des "Ressourcenausnahmeprinzips" (Resource Acquisition Is Initialization, RAII) sind. Sie ermöglichen die effiziente Übertragung von Ressourcen, wie beispielsweise dynamisch alloziertem Speicher, von einem Objekt auf ein anderes, ohne dass eine unnötige Kopie der Ressourcen erfolgt.
|
||||
|
||||
In C++ wird dies durch die Verwendung von sogenannten Rvalue-Referenzen ermöglicht, die mit dem sogenannten "Move-Semantik" verbunden sind. Die Rvalue-Referenz wird durch das "&&" -Symbol gekennzeichnet. Der Move-Konstruktor wird verwendet, um ein Objekt aus einem temporären (Rvalue-) Objekt zu konstruieren, während der Move-Assignment Operator verwendet wird, um einem bereits vorhandenen Objekt die Ressourcen eines temporären Objekts zuzuweisen.
|
||||
|
||||
```cpp
|
||||
// Move-Konstruktor
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass(MyClass&& other) {
|
||||
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```cpp
|
||||
// Move-Assignment Operator
|
||||
class MyClass {
|
||||
public:
|
||||
MyClass& operator=(MyClass&& other) {
|
||||
if (this != &other) {
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <iostream>
|
||||
|
||||
class Maschine
|
||||
{
|
||||
private:
|
||||
bool status;
|
||||
bool kaputt;
|
||||
|
||||
public:
|
||||
Maschine(bool status, bool kaputt) : status(status), kaputt(kaputt) {}
|
||||
virtual ~Maschine() {}
|
||||
|
||||
void schalter()
|
||||
{
|
||||
status = !status;
|
||||
}
|
||||
virtual void schrotten()
|
||||
{
|
||||
kaputt = true;
|
||||
}
|
||||
bool ist_an()
|
||||
{
|
||||
return status && !kaputt;
|
||||
}
|
||||
};
|
||||
|
||||
class Waschmaschine : public Maschine
|
||||
{
|
||||
private:
|
||||
public:
|
||||
Waschmaschine(bool status, bool kaputt) : Maschine(status, kaputt) {}
|
||||
~Waschmaschine() {}
|
||||
void waschen()
|
||||
{
|
||||
if (ist_an())
|
||||
{
|
||||
std::cout << "Die Waschmaschine wäscht die Wäsche ...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Die Waschmaschine ist aus oder kaputt ...\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Spuellmaschine : public Maschine
|
||||
{
|
||||
private:
|
||||
public:
|
||||
Spuellmaschine(bool status, bool kaputt) : Maschine(status, kaputt) {}
|
||||
~Spuellmaschine() {}
|
||||
void spuelen()
|
||||
{
|
||||
if (ist_an())
|
||||
{
|
||||
std::cout << "Die Spüllmaschine spüllt ...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Die Spüllmaschine ist aus oder kaputt ...\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
#include <iostream>
|
||||
|
||||
// Definiere eine Vorlage für die Klasse QueueList.
|
||||
// Diese Klasse implementiert eine Warteschlange mit einer verketteten Liste.
|
||||
template <typename E>
|
||||
class QueueList
|
||||
{
|
||||
private:
|
||||
// Definition eines Knotens, der einen Wert und einen Zeiger auf den nächsten Knoten hat.
|
||||
struct Node
|
||||
{
|
||||
E value;
|
||||
Node *next;
|
||||
};
|
||||
|
||||
// Zeiger auf den Anfang und das Ende der Warteschlange und eine Zählvariable für die Größe.
|
||||
Node *head;
|
||||
Node *tail;
|
||||
int count;
|
||||
|
||||
public:
|
||||
// Konstruktor initialisiert Zeiger und Zähler mit Nullwerten.
|
||||
QueueList() : head(nullptr), tail(nullptr), count(0) {}
|
||||
|
||||
// Destruktor löscht alle Elemente in der Warteschlange.
|
||||
// Voraussetzung: keine
|
||||
// Effekt: Alle Elemente in der Warteschlange werden gelöscht
|
||||
// Ergebnis: Keines
|
||||
~QueueList()
|
||||
{
|
||||
while (!is_empty())
|
||||
{
|
||||
dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
// Überprüft, ob die Warteschlange leer ist.
|
||||
// Voraussetzung: keine
|
||||
// Effekt: Keiner
|
||||
// Ergebnis: Wenn die Warteschlange leer ist, ist 'true' geliefert, sonst ist 'false' geliefert
|
||||
bool is_empty() const
|
||||
{
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
// Gibt die Größe der Warteschlange zurück.
|
||||
// Voraussetzung: keine
|
||||
// Effekt: Keiner
|
||||
// Ergebnis: Die Anzahl der Elemente in der Warteschlange wird zurückgegeben
|
||||
unsigned size() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
// Gibt das erste Element der Warteschlange zurück.
|
||||
// Voraussetzung: Die Warteschlange ist nicht leer
|
||||
// Effekt: Keiner
|
||||
// Ergebnis: Das vorderste Element wird zurückgegeben
|
||||
E front() const
|
||||
{
|
||||
if (is_empty())
|
||||
{
|
||||
throw std::runtime_error("Queue is empty");
|
||||
}
|
||||
return this->head->value;
|
||||
}
|
||||
|
||||
// Fügt ein Element am Ende der Warteschlange ein.
|
||||
// Voraussetzung: Keine
|
||||
// Effekt: Das übergebene Element wird hinten in die Warteschlange eingefügt
|
||||
// Ergebnis: Keines
|
||||
void enqueue(E x)
|
||||
{
|
||||
Node *newNode = new Node();
|
||||
newNode->value = x;
|
||||
newNode->next = nullptr;
|
||||
|
||||
if (tail != nullptr)
|
||||
{
|
||||
tail->next = newNode;
|
||||
}
|
||||
|
||||
tail = newNode;
|
||||
|
||||
if (is_empty())
|
||||
{
|
||||
head = newNode;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
// Entfernt das erste Element der Warteschlange und gibt seinen Wert zurück.
|
||||
// Voraussetzung: Die Warteschlange ist nicht leer
|
||||
// Effekt: Das vorderste Element wird aus der Warteschlange entfernt
|
||||
// Ergebnis: Das vorderste Element wird zurückgegeben
|
||||
E dequeue()
|
||||
{
|
||||
if (is_empty())
|
||||
{
|
||||
throw std::runtime_error("Queue is empty");
|
||||
}
|
||||
|
||||
Node *tempNode = head;
|
||||
E value = head->value;
|
||||
|
||||
head = head->next;
|
||||
|
||||
if (head == nullptr)
|
||||
{
|
||||
tail = nullptr;
|
||||
}
|
||||
|
||||
delete tempNode;
|
||||
count--;
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// Erstellt eine Warteschlange und fügt die Werte 3, 5 und 2 hinzu. Dann wird das vorderste Element angezeigt und zwei Elemente werden entfernt.
|
||||
// Überprüft schließlich, ob die Warteschlange leer ist.
|
||||
QueueList<int> q;
|
||||
q.enqueue(1);
|
||||
q.enqueue(2);
|
||||
q.enqueue(3);
|
||||
std::cout << q.front() << std::endl; // Erwartetes Ergebnis: 1
|
||||
std::cout << q.dequeue() << std::endl; // Erwartetes Ergebnis: 1
|
||||
std::cout << q.dequeue() << std::endl; // Erwartetes Ergebnis: 2
|
||||
std::cout << q.is_empty() << std::endl; // Erwartetes Ergebnis: 0
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
KLassenhierarchie
|
||||
|
||||
1. tier
|
||||
Attribute: gewicht, groesse
|
||||
Methoden: fortbewegen(), atmen(), essen()
|
||||
1.1. saeugetier
|
||||
1.1.1. hund
|
||||
Attribute: gattung
|
||||
Methoden: anbellen(tier t), fortpflanzen(hund h)
|
||||
1.1.1.1. pudel
|
||||
1.2. insekt
|
||||
1.2.1. biene
|
||||
*/
|
||||
|
||||
/*
|
||||
Klasse Tier: Basis-Klasse in der Klassenstruktur.
|
||||
Kann genutzt werden, um allgemeine Eigenschaften und Verhaltensweisen von Tieren abzubilden.
|
||||
Wir haben die Methoden essen(), atmen(), und fortbewegen() als virtual in der Basisklasse Tier definiert, was bedeutet, dass sie in den abgeleiteten Klassen überschrieben werden koennen, wenn dies gewünscht wird.
|
||||
*/
|
||||
class Tier
|
||||
{
|
||||
public:
|
||||
int gewicht;
|
||||
int groesse;
|
||||
|
||||
// Konstruktor: Kann aufgerufen werden, um ein neues Tierobjekt mit definiertem Gewicht und Groesse zu erstellen.
|
||||
// Gibt ein neues Tierobjekt zurück.
|
||||
// Fügt dem Programmzustand ein neues Tierobjekt hinzu.
|
||||
Tier(int gewicht, int groesse) : gewicht(gewicht), groesse(groesse) {}
|
||||
|
||||
// Destruktor: Wird aufgerufen, wenn ein Tierobjekt geloescht wird.
|
||||
// Gibt nichts zurück.
|
||||
// Entfernt das Tierobjekt aus dem Zustand des Programms.
|
||||
~Tier() {}
|
||||
|
||||
// Kann aufgerufen werden, um das Verhalten eines Tiers zu simulieren, sich fortzubewegen.
|
||||
// Gibt Text zurück und hat keine Effekte.
|
||||
virtual void fortbewegen()
|
||||
{
|
||||
std::cout << "Tier bewegt sich fort." << std::endl;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um das Verhalten eines Tiers zu simulieren, zu atmen.
|
||||
// Gibt Text zurück und hat keine Effekte.
|
||||
virtual void atmen()
|
||||
{
|
||||
std::cout << "Tier atmet." << std::endl;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um das Verhalten eines Tiers zu simulieren, zu essen.
|
||||
// Gibt Text zurück und hat keine Effekte.
|
||||
virtual void essen()
|
||||
{
|
||||
std::cout << "Tier isst." << std::endl;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um das Gewicht des Tiers zu ermitteln.
|
||||
// Gibt das Gewicht des Tiers als Integer zurück.
|
||||
// Ändert den Zustand des Programms nicht.
|
||||
int getGewicht()
|
||||
{
|
||||
return gewicht;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um die Groesse des Tiers zu ermitteln.
|
||||
// Gibt die Groesse des Tiers als Integer zurück.
|
||||
// Ändert den Zustand des Programms nicht.
|
||||
int getGroesse()
|
||||
{
|
||||
return groesse;
|
||||
}
|
||||
};
|
||||
|
||||
// Klasse Saeugetier (Säugetier): Erbt von der Klasse Tier.
|
||||
// Kann genutzt werden, um ein spezifisches Tier zu erstellen, das ein Säugetier ist.
|
||||
class Saeugetier : public Tier
|
||||
{
|
||||
public:
|
||||
// Konstruktor: Kann aufgerufen werden, um ein neues Saeugetier-Objekt mit definiertem Gewicht und Groesse zu erstellen.
|
||||
// Gibt ein neues Saeugetier-Objekt zurück.
|
||||
// Fügt dem Programmzustand ein neues Saeugetier-Objekt hinzu.
|
||||
Saeugetier(int gewicht, int groesse) : Tier(gewicht, groesse) {}
|
||||
};
|
||||
|
||||
// Klasse Hund: Erbt von der Klasse Saeugetier.
|
||||
// Kann genutzt werden, um ein spezifisches Tier zu erstellen, das ein Hund ist.
|
||||
class Hund : public Saeugetier
|
||||
{
|
||||
|
||||
public:
|
||||
std::string gattung;
|
||||
|
||||
// Konstruktor: Kann aufgerufen werden, um ein neues Hund-Objekt mit definiertem Gewicht, Groesse und Gattung zu erstellen.
|
||||
// Gibt ein neues Hund-Objekt zurück.
|
||||
// Fügt dem Programmzustand ein neues Hund-Objekt hinzu.
|
||||
Hund(int gewicht, int groesse, std::string gattung) : Saeugetier(gewicht, groesse), gattung(gattung) {}
|
||||
|
||||
// Destruktor: Wird aufgerufen, wenn ein Hund-Objekt geloescht wird.
|
||||
// Gibt nichts zurück.
|
||||
// Entfernt das Hund-Objekt aus dem Zustand des Programms.
|
||||
~Hund() {}
|
||||
|
||||
// Kann aufgerufen werden, um das Verhalten eines Hundes zu simulieren, ein anderes Tier anzubellen.
|
||||
// Gibt Text zurück und hat keine Effekte.
|
||||
void anbellen(Tier t)
|
||||
{
|
||||
std::cout << "Hund bellt anderen Hund an" << std::endl;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um das Verhalten eines Hundes zu simulieren, sich fortzupflanzen.
|
||||
// Gibt Text zurück und hat keine Effekte.
|
||||
void fortpflanzen(Hund h)
|
||||
{
|
||||
std::cout << "Hund pflanzt sich mit anderem Hund fort" << std::endl;
|
||||
}
|
||||
|
||||
// Kann aufgerufen werden, um die Gattung des Hundes zu ermitteln.
|
||||
// Gibt die Gattung des Hundes als String zurück.
|
||||
// Ändert den Zustand des Programms nicht.
|
||||
std::string getGattung()
|
||||
{
|
||||
return gattung;
|
||||
}
|
||||
};
|
||||
|
||||
// Klasse Pudel: Erbt von der Klasse Hund.
|
||||
// Kann genutzt werden, um ein spezifisches Tier zu erstellen, das ein Pudel ist.
|
||||
class Pudel : public Hund
|
||||
{
|
||||
public:
|
||||
// Konstruktor: Kann aufgerufen werden, um ein neues Pudel-Objekt mit definiertem Gewicht, Groesse und Gattung zu erstellen.
|
||||
// Gibt ein neues Pudel-Objekt zurück.
|
||||
// Fügt dem Programmzustand ein neues Pudel-Objekt hinzu.
|
||||
Pudel(int gewicht, int groesse, std::string gattung) : Hund(gewicht, groesse, gattung) {}
|
||||
|
||||
// Destruktor: Wird aufgerufen, wenn ein Pudel-Objekt geloescht wird.
|
||||
// Gibt nichts zurück.
|
||||
// Entfernt das Pudel-Objekt aus dem Zustand des Programms.
|
||||
~Pudel() {}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Pudel Bello(10, 45, "Pudel");
|
||||
Pudel Strolch(20, 60, "Pudel");
|
||||
|
||||
int gewichtBello = Bello.getGewicht();
|
||||
int groesseBello = Bello.getGroesse();
|
||||
std::string gattungBello = Bello.getGattung();
|
||||
|
||||
std::cout << "Ein Hund der gattung " << gattungBello << ", mit der groesse " << groesseBello << " und mit dem Gewicht " << gewichtBello << " wurde erstellt" << std::endl;
|
||||
|
||||
Bello.essen();
|
||||
Bello.atmen();
|
||||
Bello.fortbewegen();
|
||||
Bello.anbellen(Strolch);
|
||||
Bello.fortpflanzen(Strolch);
|
||||
}
|
||||
Reference in New Issue
Block a user