added ws202425 courses

This commit is contained in:
2024-11-14 13:11:04 +01:00
parent 800282c2e8
commit beb31897b1
461 changed files with 20368 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
# Abwägen
## 1.
Wenn Sie nur jeweils eine Rolle gegen eine andere abwägen dürfen, dann ist die Methode der **binären Suche** das effizienteste Verfahren. Hier ist, wie Sie es tun könnten:
Vergleichen Sie die Rollen 1 und 2. Wenn sie gleich schwer sind, gehen Sie zur Rolle 3 und so weiter. Wenn sie unterschiedlich schwer sind, haben Sie das Falschgeld gefunden.
Wiederholen Sie diesen Vorgang, bis Sie das Falschgeld gefunden haben.
Im schlimmsten Fall müssten Sie 11 Vergleiche durchführen, um das Falschgeld zu finden (wenn das Falschgeld in der letzten Rolle ist).
## 2.
Wenn Sie mehrere Rollen gegeneinander wägen dürfen, dann können Sie das Problem mit nur 3 Vergleichen lösen. Hier ist ein möglicher Weg:
- Vergleich 1: Nehmen Sie die ersten 4 Rollen (1-4) und wägen Sie sie gegen die nächsten 4 Rollen (5-8). Es gibt zwei Möglichkeiten:
- Wenn sie gleich schwer sind, liegt das Falschgeld in den Rollen 9-12. Gehen Sie zu Vergleich 2a.
- Wenn sie unterschiedlich schwer sind, liegt das Falschgeld in den Rollen 1-8. Gehen Sie zu Vergleich 2b.
- Vergleich 2a: Wägen Sie die Rollen 9 und 10 gegen die Rollen 11 und 12. Hier finden Sie heraus, welche Rolle das Falschgeld enthält und ob es leichter oder schwerer ist.
- Vergleich 2b: Wägen Sie drei der verdächtigen Rollen (zum Beispiel 1, 2, 3) gegen drei Rollen, von denen Sie wissen, dass sie echt sind (zum Beispiel 9, 10, 11). Es gibt zwei Möglichkeiten:
- Wenn sie gleich schwer sind, liegt das Falschgeld in der verbliebenen verdächtigen Rolle (4). Sie wissen auch, dass das Falschgeld schwerer ist, weil die verdächtigen Rollen bei dem ersten Vergleich schwerer waren.
- Wenn sie unterschiedlich schwer sind, liegt das Falschgeld in den drei verdächtigen Rollen (1, 2, 3). Gehen Sie zu Vergleich 3.
- Vergleich 3: Wägen Sie zwei der verdächtigen Rollen (zum Beispiel 1 und 2) gegeneinander. Hier finden Sie heraus, welche Rolle das Falschgeld enthält und ob es leichter oder schwerer ist.
+35
View File
@@ -0,0 +1,35 @@
/* Informatik B - Sommersemester 2022
Vorlesung am 21.04.2022
Katharina Klost
Beispiele zu arithmetischen Operationen
*/
#include <iostream>
int main(){
/* Unterschiede zwische Post- und Preinkrement:
Beim Postinkrement wird der Wert *erst* ausgewertet und dann erhöht,
daher wird in der zweiten Zeile noch einmal 2 und erst in der dritten
Zeile 3 ausgegeben.
Bei Preinkrement wird erst erhöht und dann ausgegeben, daher wird in der
vierten und fünften Zeile 4 ausgegeben.
*/
int n =2;
std::cout << "Vor Postinkrement " << n << std::endl;
std::cout << "Postinkrement " << n++ << std::endl;
std::cout << "Nach Postinkrement " << n << std::endl;
std::cout << "Preinkrement " << ++n << std::endl;
std::cout << "Nach Preinkrement " << n << std::endl;
// += als vereinfachte Schreibweise für n = n + 3;
n+=3;
std::cout << n << std::endl;
// k und k1 haben auf Grund der Bindunststärken den gleichen Wert
int k1 = 7*4 + ++n/4;
int k = (7*4)+ ((++n) / 4);
std::cout << "k1: " << k1 << ", k: " <<k << std::endl;
}
+73
View File
@@ -0,0 +1,73 @@
/* Informatik B - Sommersemester 2022
Vorlesung am 21.04.2022
Katharina Klost
Beispiele zu Datentypen
*/
#include <iostream>
#include <climits>
int main(){
//Auf Grund der Wertsemantik wird nur der Inhalt von num 3 verändert
int num2 = 20;
int num3 = num2;
std::cout << "num 2: " << num2 << std::endl;
std::cout << "num 3: " << num3 << std::endl;
num3 = num3 +1;
std::cout << "num 2: " << num2 << std::endl;
std::cout << "num 3: " << num3 << std::endl;
/*Auch wenn in einem Bool allgeine Werte gespeichert werden können
wird nur 0 für false und 1 für alle Werte != 0 ausgegeben*/
bool b1 = 12;
std::cout << "b1: " << b1 << std::endl;
std::cout << "Größe von b1: " << sizeof(b1) << std::endl;
//Da chars intern als Zahlen gespeichert werden, kann mit diesen auch
//gerechnet werden
char c1 = 'A';
std::cout << "c1: " << c1 << std::endl;
c1=c1+1;
std::cout << "c1: " << c1 << std::endl;
//Bei Integern kann es zu einem Überlauf kommen
int n = INT_MAX;
std::cout << n << std::endl;
n= n+1;
std::cout << n << std::endl;
// Bei Floats kann es wegen Rundungsfehlern zu unerwartetem Verhalten kommen
float x = 0;
for (int i = 0; i < 10; i++){
x = x + 0.1;
std::cout << "x"<<i <<" :"<< x << std::endl;
}
std::cout <<"(x==1.0) " << (x==1.0) << std::endl;
//Größen von verschiedenen Datentypen
short s1 = 123;
long l1 = 123;
long long ll1 = 123;
float f1 = 123.0;
double d1 = 123.0;
long double ld1 = 123.0;
// Größen der Datentypen (auf diesem System)
std::cout << "Größen verschiedener Datentypen in Byte" << std::endl;
std::cout << "bool: " << sizeof(b1) << std::endl;
std::cout << "char: " << sizeof(c1) << std::endl;
std::cout << "short: " << sizeof(s1) << std::endl;
std::cout << "int: " << sizeof(n) << std::endl;
std::cout << "long: " << sizeof(l1) << std::endl;
std::cout << "long long: " << sizeof(ll1) << std::endl;
std::cout << "float: " << sizeof(f1) << std::endl;
std::cout << "double: " << sizeof(d1) << std::endl;
std::cout << "long double: " << sizeof(ld1) << std::endl;
std::cout << "Größe eines Bytes in Bit: " << CHAR_BIT << std::endl;
}
+13
View File
@@ -0,0 +1,13 @@
/* Informatik B - Sommersemester 2022
Vorlesung am 21.04.2022
Katharina Klost
Beispiele zur Ein-und Ausgabe
*/
#include <iostream>
int main(){
/* Der Text "Hello World!" wird auf der Konsole ausgegeben, gefolgt von einem
Zeilenumbruch*/
std::cout << "Hello World!" << std::endl;
}
+19
View File
@@ -0,0 +1,19 @@
/* Informatik B - Sommersemester 2022
Vorlesung am 21.04.2022
Katharina Klost
Beispiele zur Ein-und Ausgabe
*/
#include <iostream>
int main(){
float a;
//Ohne std::endl gibt es keinen Zeilenumbruch
std::cout << "Gebe eine Zahl ein: ";
// mit std::cin wird ein Wert von der Konsole gelesen und wenn möglich
// in die passende Variable geschrieben
std::cin >> a;
// Das Ergebnis von Ausdrücken kann auf der Konsole ausgegeben werden
std::cout << "a: " << a << std::endl;
std::cout << "a mal zwei: " << a*2 << std::endl;
}
+22
View File
@@ -0,0 +1,22 @@
#include <iostream>
bool isOdd(int num){
return num%2;
}
int main(){
std::cout << "Hello Günter" << std::endl;
int x = 16;
for(int i = 0; i<x; i++){
std::cout << "i: " << i;
if(isOdd(i)){
std::cout << " ist gerade" << std::endl;
}
else{
std::cout << " ist nicht gerade" << std::endl;
}
}
}
+41
View File
@@ -0,0 +1,41 @@
# Aufgabe 1: C++
## 1.
> Betrachten Sie die Datei tut1.cpp aus dem Whiteboard und kompilieren Sie diese mit
```bash
g++ -std=c++14 -Wall -Wextra -Werror -pedantic tut1.cpp -o tut1.
```
## 2.
> Verändern Sie die erste Zeile die ausgegeben wird zu einer beliebigen anderen Nachricht. Aktuell zählt das Programm bis 9 hoch. Passen Sie es so an, dass es bis 15 zählt.
tut1.cpp
# Aufgabe 2: Abwägen
> Vor Ihnen liegen 12 Rollen mit 1-Euro-Münzen, die Rollen sind durchnummeriert von 1 bis 12. Genau eine Rolle enthält Falschgeld und hat deswegen ein anderes Gewicht. Ihre Aufgabe ist es mittels einer Balkenwaage festzustellen, welche der Rollen das Falschgeld enthält und ob diese leichter oder schwerer ist als eine normale Rolle.
## 1.
> Sie dürfen nur jeweils eine Rolle gegen eine andere abwägen. Beschreiben Sie ein Verfahren, das möglichst wenige Vergleiche verwendet. Wie viele sind das im schlechtesten Fall und warum geht es nicht besser?
Nimm eine Referenzrolle und wiege alle gegen diese. 1 davon wiegt mehr oder weniger, dann weis man welche es ist.
Min: 1 Versuch
Max 11 Versuche
## 2.
> Sie dürfen mehrere Rollen gegeneinander wägen. Jetzt reichen 3 Vergleiche aus! Beschreiben Sie ein solches Verfahren.
Hinweis: Beginen Sie mit vier Rollen auf jeder Seite. Der schwierige Fall tritt bei Ungleichheit ein, da Sie dann noch acht Verdächtige haben. Wählen Sie in diesem Fall aus diesen acht Verdächtigen sechs aus und verteilen Sie diese geschickt auf der Waage.
4_1 zu 4_2
wenn gleich{
4_3 aufteilen
2 zu 2
1 zu 1
}
wenn ungleich:{
fahre fort
2 zu 2
1 zu 1
}
+44
View File
@@ -0,0 +1,44 @@
#include <iostream>
int main(){
int x = 2;
for (int i=0; i<32; i++){
x=x*2;
}
std::cout << x << std::endl;
int zahl = 50;
std::cout << (3+5*9==zahl-2) << std::endl;
std::cout << "Info B " << (40 * zahl + 23) << std::endl;
std::cout << (true && (++zahl > 50)) << std::endl;
std::cout << zahl << std::endl;
std::cout << ((zahl <= 44) + 7) << std::endl;
int y = 3;
int z = ++y + 2;
std::cout << "z: " << z << std::endl;
std::cout << "y: " << y << std::endl;
z = y++ + 2;
std::cout << "z: " << z << std::endl;
std::cout << "y: " << y << std::endl;
short a = 345;
std::cout << "Groesse von a " << sizeof(a) << std::endl;
std::cout << "Groesse von a+y " << sizeof(a+y) << std::endl;
std::cout << "Geben Sie eine Zahl ein: ";
std::cin >> a;
std::cout << "Ihre Zahl ist: " << a <<std::endl;
if(++y == 6){
std::cout << "Huhu" << std::endl;
}
std::cout << "y: " << y << std::endl;
if(y-- == 6){
std::cout << "Hallo" << std::endl;
}
std::cout << "y: " << y << std::endl;
}
+203
View File
@@ -0,0 +1,203 @@
# 1. Zahlenraten
In dieser Aufgabe schauen wir uns ein einfaches Spiel für zwei Spieler:innen an. Nennen wir die Spieler:innen Alice und Bob. Alice denkt sich eine Zahl zwischen 0 und 1022 aus und Bob versucht die Zahl zu erraten. Bob darf eine Zahl nennen, und Alice gibt dann die Antwort kleiner, größer oder richtig, je nach dem ob die von Bob genannte Zahl kleiner, größer oder gleich der Zahl von Alice ist.
## (a)
> Geben Sie eine Strategie an, mit der Bob mit maximal 1022 Versuchen die Zahl errät. Argumen- tieren Sie in einem Satz, warum jede Strategie, die 1023 oder mehr Versuche braucht unnötige Fragen verwendet.
Eine Strategie wäre es immer die in die Mitte der Zahlenkette zu raten und dann einen Teil zu verwerfen. Da die Zahlenkette immer halbiert wird, wird die Anzahl der Versuche immer kleiner.
Eine Strategie mit mehr als 1023 Versuchen beinhaltet mehr Versuche als Zahlen in der Zahlenkette. Das heist selbst wenn man jede einzelne Zahl raten würde und die letzte die richtige Zahl ist, liegt man bei 1020 versuchen. Wenn man 1023 Versuche braucht fragt man also doppelt oder mehrmals nach Zahlen.
## (b)
> Wie kann Bob einfach die Informationen, die er bis zu einem Zeitpunkt gesammelt hat verwenden, um sicher zu stellen, dass er keine Zahl rät, die nicht mehr die Lösung sein kann, da sie in einem Zahlenbereich liegt, der bereits ausgeschlossen wurde.
Nicht mehr die Zahlen raten die größer oder kleiner der genannten Zahl ist. Das ist abhängig von Alice Antwort.
## (c)
> Nun ist eine Strategie gesucht, mit der das Spiel schneller vorbei ist. Bob behauptet, dass er immer maximal 10 Versuche braucht. Geben Sie eine Strategie an, die immer maximal 10 Versuche braucht. Überlegen Sie dafür, wie Sie in jedem Schritt möglichst viele Zahlen ausschließen können, unabhängig davon, welche Antwort Alice gibt. Argumentieren Sie, warum die Strategie maximal 10 Versuche braucht.
Mit der selben Strategie, die in (a) genannt wurde. Beweis:
1020/2 = 510
510/2 = 255
255/2 = 127
127/2 = 63
63/2 = 31
31/2 = 15
15/2 = 7
7/2 = 3
3/2 = 1
# 2. Lesen und Anpassen von C++ Code (0+6+2+2 Punkte)
## (a)
> Betrachten Sie die Datei ueb1.cpp aus dem Whiteboard und kompilieren Sie diese mit
```bash
g++ -std=c++14 -Wall -Wextra -Werror -pedantic ueb1.cpp -o ueb1
```
## (b)
> Führen Sie das Programm mit ./ueb1 aus und erklären Sie den Ablauf anhand des Quellcodes.
```cpp
int main(){
```
Die main Funktion ist die Funktion die beim starten des Programms ausgeführt wird. Sie ist die erste Funktion die aufgerufen wird.
```cpp
int x = 2;
for (int i=0; i<32; i++){
x=x*2;
}
std::cout << x << std::endl;
```
Die Variable x wird mit dem Wert 2 initialisiert. Die for Schleife wird 32 mal ausgeführt. In jeder Iteration wird x mit 2 multipliziert. Das Ergebnis sollte 4,294,967,296 sein. Da der Datentype int aber nur 32 bit hat und da die höchste Zahl 2,147,483,647 ist, kommt es zu einem Overflow. Der Wert von x wird zurückgesetz auf 0, was ausgegeben wird.
```cpp
int zahl = 50;
std::cout << (3+5*9==zahl-2) << std::endl;
std::cout << "Info B " << (40 * zahl + 23) << std::endl;
std::cout << (true && (++zahl > 50)) << std::endl;
std::cout << zahl << std::endl;
std::cout << ((zahl <= 44) + 7) << std::endl;
```
Die Variable zahl wird mit dem Wert 50 initialisiert. Die zweite Zeile gibt 1 aus, da die Bedingung wahr ist. Die dritte Zeile berechnet den Wert 2023 und gibt diesen mit dem String "Info B" aus. Die vierte Zeile gibt 1 aus, da die Bedingung wahr ist. Die fünfte Zeile gibt 51 aus, da die Variable zahl in der vorherigen Zeile um 1 erhöht wurde. Die sechste Zeile gibt 7 aus, da die Bedingung falsch ist und 0 + 7 = 7 ist.
```cpp
int y = 3;
int z = ++y + 2;
std::cout << "z: " << z << std::endl;
std::cout << "y: " << y << std::endl;
z = y++ + 2;
std::cout << "z: " << z << std::endl;
std::cout << "y: " << y << std::endl;
```
Dieser Codeteil soll den Unterschied zwischen ++y und y++ zeigen. ++y erhöht y um 1 und wird dann verwendet. y++ wird zuerst verwendet und dann erhöht.
Die Variable y wird mit dem Wert 3 initialisiert. In der zweiten Zeile wird z mit dem Wert 6 initialisiert. Da y um 1 erhöhrt wird und dann mit 2 addiert wird. y hat jetzt den Wert 4 und z den Wert 6 was ausgegeben wird. In der fünften Zeile wird z mit dem Wert 6 initialisiert. Da y immernoch den Wert 4 hat und mit 2 addiert wird. Nach dem Ausdruck wird y um 1 erhöht. y hat jetzt den Wert 5 und z den Wert 6 was ausgegeben wird.
```cpp
int a = 345;
std::cout << "Groesse von a " << sizeof(a) << std::endl;
std::cout << "Groesse von a+y " << sizeof(a+y) << std::endl;
```
Zeigt die Größe von a und a+y in Bytes an.
```cpp
std::cout << "Geben Sie eine Zahl ein: ";
std::cin >> a;
std::cout << "Ihre Zahl ist: " << a <<std::endl;
```
Fragt den Benutzer nach einer Zahl und gibt diese aus.
```cpp
if(y++ == 6){
std::cout << "Huhu" << std::endl;
}
std::cout << "y: " << y << std::endl;
if(--y == 6){
std::cout << "Hallo" << std::endl;
}
std::cout << "y: " << y << std::endl;
}
```
Gibt "Hallo" oder "Huhu" aus, je nachdem ob die Bedingung wahr oder falsch ist. Danach wird y ausgegeben.
## (c)
> Passen Sie das Programm so an, dass beide if-Bedingungen zu wahr auswerten, ohne dass sich der Zustand der Variablen nach den if-Bedingungen ändert.
```cpp
if(++y == 6){
std::cout << "Huhu" << std::endl;
}
std::cout << "y: " << y << std::endl;
if(y-- == 6){
std::cout << "Hallo" << std::endl;
}
std::cout << "y: " << y << std::endl;
```
## (d)
> Das Programm führt keine adäquate Fehlerbehandlung aus. Finden Sie Eingaben, bei denen das Programm ein unerwünschtes Verhalten zeigt.
Todo!
# 3. Von-Neumann-Rechner (5+5 Punkte)
## (a)
> Schreiben Sie ein Programm für den Von-Neumann-Rechner, das solange eine Variable hochzählt, bis diese den gleichen Wert wie eine gelesene Eingabe hat. Kommentieren Sie die Bedeutung der Speicherzellen.
> Sie können die folgenden Befehle verwenden:
| Befehl | Beschreibung |
|----------|----------------------------------------------------------------------------|
| READ | Liest eine Eingabe ein. |
| WRITE X | Schreibt den Inhalt des Rechenwerks in Zelle X. |
| LOAD X | Liest den Inhalt von Zelle X und schreibt diesen ins Rechenwerk. |
| CJUMP X | Setzt den Befehlszähler auf X, wenn der Inhalt des Rechenwerks 0
ist. |
| GOTO X | Setzt den Befehlszähler auf X. |
| ADD X | Addiert den Inhalt von Zelle X auf den Inhalt des Rechenwerks. |
| SUB X | Subtrahiert den Inhalt von Zelle X vom Inhalt des Rechenwerks. |
| OUTP X | Gibt den Inhalt von Zelle X aus. |
| RETURN | Beendet das Programm. |
```Von-Neumann-Rechner
1: READ
2: WRITE 10
3: LOAD 11
4: ADD 12
5: STORE 11
6: SUB 10
7: CJUMP 9
8: JUMP 11
9: RETURN
10: 0
11: 0
12: 1
```
## (b)
> Beschreiben Sie, was das folgende Programm berechnet.
```
0: READ INP
1: WRITE 14
2: READ INP
4: WRITE 15
5: CJUMP 13
6: READ 16
7: ADD 14
8: WRITE 16
9: READ 15
10: SUB 17
11: WRITE 15
12: JUMP 5
13: OUTP 16
14:0
15:0
16:0
17:1
```
Liest zwei Zahlen ein. Die eine wird in einer Schleife verdoppelt, die andere wird um 1 reduziert. Wenn die zweite Zahl 0 erreicht, wird die mehrmals verdoppelte Zahl ausgegeben.
```
x = get()
y = get()
while y != 0:
z = 0
z = z + x
y = y - 1
print()
```
+29
View File
@@ -0,0 +1,29 @@
#include <iostream>
using namespace std;
int main(){
int n1 = 10;
int n2 = 20;
int n3 = n1/n2;
float f1 = 10;
float f2 = f1 + 10;
float f3 = f1/f2;
cout << "n3: " << n3 << endl;
cout << "f3: " << f3 << endl;
f3 += 3;
cout << "f3: " << f3++ << endl;
cout << "f3: mit postincr " << f3++ << endl;
cout << "f3: mit preincr " << ++f3 << endl;
bool comp1 = 2*5+5*2 == n2;
n2 = n2*2;
bool comp2 = 2*5+5*2 == n2;
bool comp3 = n3 < f3;
bool comp4 = comp1 && comp3 || comp2;
cout << "comp1, comp2, comp3, comp4: " << comp1 << ", " << comp2 << ", " << comp3 << ", " << comp4 << endl;
}
+54
View File
@@ -0,0 +1,54 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zur Kombination von zusammengesetzen Datentypen
*/
#include <iostream>
#include <vector>
#include <string>
enum class wohnart{HAUS,WOHNUNG};
struct person{
std::string name;
wohnart wa;
unsigned telefonnummer;
};
int main(){
// eine person hat unter anderem einen enum als Feld und kann in einem
// vector gespeichert werden
std::vector<person> telefonbuch = {};
for(unsigned i=0; i< 30; i++){
person p = {"Name"+std::to_string(i) , static_cast<wohnart>((3*i-1)%2), (i*300+23+i/2)};
telefonbuch.push_back(p);
}
person eintrag;
for(unsigned i= 0 ; i< telefonbuch.size(); i++){
eintrag = telefonbuch[i];
if(eintrag.wa == wohnart::HAUS){
std::cout << eintrag.name << " wohnt in einem Haus" << std::endl;
}
}
std::string query;
std::cout << "Welcher Name soll gesucht werden? " ;
std::cin >> query;
bool found = false;
for (unsigned i= 0; i< telefonbuch.size()&&!found; i++){
eintrag = telefonbuch[i];
if (eintrag.name == query){
std::cout << eintrag.name << " hat Telefonnummer: "<< eintrag.telefonnummer << std::endl;
found = true;
}
}
if (!found){
std::cout << query << " ist nicht im Telefonbuch enthalten" << std::endl;
}
}
+19
View File
@@ -0,0 +1,19 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 26.04.2023
Katharina Klost
Beispiele zur Deklaration und Initialisierung von Variablen
*/
#include <iostream>
int main(){
int num1;
//num1 ist nur deklariert und nicht initialisiert und daher ist
//der Inhalt hier nicht definiert (irgendein Wert)
std::cout << num1 << std::endl;
num1 = 10;
std::cout << num1 << std::endl;
}
+66
View File
@@ -0,0 +1,66 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu enums
*/
#include <iostream>
//Ein enum für Wochentage
enum class Wochentag{
Montag,
Dienstag,
Mittwoch,
Donnerstag,
Freitag,
Samstag,
Sonntag
};
//Ein enum für Farben
enum class Farbe {rot, gruen, blau, orange};
// Ein enum für die Zustände von Ampeln
enum class Ampel {gruen, gelb, rot, gelb_rot};
int main(){
// der Name des enums muss immer angegeben werden, da es Felder mit
// dem gleichen Namen in verschiedenen enums geben kann
Ampel ampel1 = Ampel::gruen;
//enums und switch-case sind eine beliebte Kombination
switch(ampel1){
case Ampel::gruen:
std::cout << "Kann fahren" << std::endl;
break;
case Ampel::gelb:
std::cout << "Achtung" << std::endl;
break;
case Ampel::rot:
std::cout << "Stop" << std::endl;
break;
case Ampel::gelb_rot:
std::cout << "Achtung" << std::endl;
break;
default:
std::cout <<"Irgendwas anderes" << std::endl;
break;
}
int number;
std::cout << "Gebe einen Tag ein, (Montag=1,..., Sonntag =7)";
std::cin >> number;
// da enums intern mit Zahlen dargestellt werden, können Zahlen
// explizit in eine enum umgewandelt werden
Wochentag tag = static_cast<Wochentag>(number-1);
// werden enums verglichen gilt die Reihenfolge in der Definition
if (tag >= Wochentag::Samstag){
std::cout << "Es ist Wochenende :)" << std::endl;
}
else{
std::cout << "Es ist ein Arbeitstag :(" << std::endl;
}
}
+21
View File
@@ -0,0 +1,21 @@
#include <iostream>
// Fibonacci numbers
int main() {
int x;
std::cout << "x-th Fibonacci number?: ";
std::cin >> x;
int prev = 0;
int curr = 1;
int tmp;
for (int i = 2; i <= x; ++i) {
tmp = curr;
curr = prev + curr;
prev = tmp;
}
}
+49
View File
@@ -0,0 +1,49 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 26.04.2023
Katharina Klost
Beispiele zu Gültigkeitsbereichen
*/
#include <iostream>
int main (){
int variable1;
std::cin >> variable1;
float variable3;
if (variable1 == 10){
float varible2 = 9.1;
std::cout << varible2 << std::endl;
}
else{
// neue Deklaration von variable3, überschattet die andere variable3
float variable3 = 10.1;
std::cout << variable3 << std::endl;
}
/*
variable2 ist nur in der ersten if-Bedingung definiert und kann daher
hier nicht verwendet werden.
*/
//std::cout << variable2 << std::endl;
//Da diese variable3 nur deklariert aber nicht initialisiert wurde,
// ist der Inhalt hier beliebig
std::cout << variable3 << std::endl;
variable3 = 1.1;
while(variable1<10){
//wird immer wieder neu deklariert
int variable3 = variable1;
std::cout << "variable3 in schleife: " << variable3 << std::endl;
variable1++;
}
std::cout << "variable1 nach erster Schleife: " << variable1 << std::endl;
std::cout << "variable3 nach erster Schleife: " << variable3 << std::endl;
}
@@ -0,0 +1,82 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 26.04.2023
Katharina Klost
Beispiele zu Kontrollstrukturen
*/
#include <iostream>
int main(){
/*
Beispiel für eine komplexere if-then-else Abfrage.
*/
int num ;
std::cout << "Gebe eine Zahl für den if test ein: " ;
std::cin >> num;
if (num == 2) {
num = num*2;
}
else if (num > 1 && num <5) {
num= num*3;
}
else if (num >=10){
num = num*4;
}
else{
num = num*5;
}
std::cout << num << std::endl;
/*
Beispiel für eine switch-case umgebeung
Die einzelnen case Abschnitte sind keine Blöcke, daher gibt es ohne
break einen fallthrough und es können keine Variablen deklariert werden.
Bei modernen Compilern sollte es keine Unterschiede in der Performance
von semantisch gleichen if-Bedingungen und switch-case Umgebungen geben
*/
int num1;
std::cout << "Gebe eine Zahl für den switch-case test ein: ";
std::cin >> num1;
switch(num1){
case 1:
std::cout << "num ist 1" << std::endl; //kein break -> fallthrough
case 2:
std::cout << "num ist 1 oder 2" << std::endl;
break;
case 3:
std::cout << "num ist 3" << std::endl;
break;
case 4:
std::cout << "num ist 4" << std::endl;
break;
default:
std::cout << "num ist nicht 1,2,3 oder 4" << std::endl;
}
/*
Beispiel für eine for-schleife, in <Anweisung3> und <Anweisung1> (vgl. Folien)
Können auch mehrere Anweisungen mit , getrennt stehen.
*/
int num_for = 3;
for (int counter=1; counter <=4; counter=counter+1, num_for ++) {
num_for= num_for*num_for;
std::cout << "num_for in iteration " << counter << ": "<< num_for << std::endl;
}
std::cout << "num_for nach Schleife: " << num_for << std::endl;
/*
Beispiel für eine while-schleife
*/
int num_while = 2;
int goal = 300;
while (num_while < goal) {
num_while = num_while * num_while;
std::cout << "num_while after *: " << num_while << std::endl;
}
std::cout << num_while << std::endl;
}
+19
View File
@@ -0,0 +1,19 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu pairs
*/
#include <iostream>
int main(){
std::pair<std::string, int> mein_paar = {"Hallo", 1234};
std::cout << "Erster Eintrag vom mein_paar: " << mein_paar.first << std::endl;
std::cout << "Zweiter Eintrag vom mein_paar: " << mein_paar.second << std::endl;
mein_paar.first = "Test";
std::cout << "Neuer erster Eintrag: " << mein_paar.first << std::endl;
}
+23
View File
@@ -0,0 +1,23 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu Strings
*/
#include <iostream>
#include <string>
int main(){
std::string ein_string = "Hallo";
char stelle2 = ein_string[2];
std::cout << "An index 2 steht: " << stelle2 << std::endl;
ein_string[2]= 'x';
std::cout << "Stelle 2 verändert: " << ein_string << std::endl;
ein_string = ein_string + " Welt!";
std::cout << ein_string << std::endl;
std::cout << ein_string + 'c' << std::endl;
std::cout << "Der String \"" << ein_string << "\" hat " << ein_string.length() << " Zeichen" << std::endl;
}
+49
View File
@@ -0,0 +1,49 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu structs
*/
#include <iostream>
#include <vector>
struct studi{
std::string name;
int matrikelnummer;
bool bachelor;
std::string studienfach;
};
struct studi2{
std::string name;
int matrikelnummer;
bool bachelor;
std::string studienfach;
};
int main(){
studi luca = {"Luca Muster", 42, true, "Informatik"};
studi mo = {"Mo", 112, true, "Bioinformatik"};
studi alex = {"Alex Müller", 4711, false, "Informatik"};
//mit dem . kann auf ein Feld zugegriffen werden.
std::cout << mo.name << std::endl;
mo.bachelor = false;
std::cout << mo.bachelor << std::endl;
// wegen der Wertsemantik werden die Einträge beim zuweisen kopiert
studi luca2 = luca;
luca2.name = "Luca Mustermann";
std::cout << luca2.name << std::endl;
std::cout << luca.name << std::endl;
studi2 luca3;
//nur structs vom gleichen Typ können einander zugewiesen werden
//luca3 = luca;
//structs können auch in vectoren gespeichert werden
std::vector<studi> tutorium ={luca,mo};
tutorium.push_back(alex);
}
+59
View File
@@ -0,0 +1,59 @@
#include <iostream>
#include <string>
int main() {
// Aufgabe 1a
int x;
std::cin >> x;
int y = 1;
for (int i = x; i > 0; i--) {
y = y * i;
}
std::cout << y << std::endl;
// Aufgabe 1b
int z = 1;
int l = x;
while (l > 1) {
z = z * l;
l--;
}
std::cout << z << std::endl;
// Aufgabe 2
// 1 Load 7
// 2 Write 6
// 3 Add 7
// 4 Jump 2
// 5 Return
// 6 0
// 7 3
// Aufgabe 3
std::string artist;
std::cout << "Wer ist dein Lieblingsartist?" << std::endl;
std::cin >> artist;
std::cout << "Dein Lieblingsartist ist " << artist << std::endl;
std::string album;
std::cout << "Was ist dein Lieblingsalbum?" << std::endl;
std::cin >> album;
std::cout << "Dein Lieblingsalbum ist " << album << std::endl;
int zahl;
std::cout << "Was ist deine Lieblingszahl?" << std::endl;
std::cin >> zahl;
int quad = zahl * zahl;
std::cout << "Deine Lieblingszahl ist " << quad << std::endl;
}
+59
View File
@@ -0,0 +1,59 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 26.04.2023
Katharina Klost
Beispiele zur Ein-und Ausgabe
*/
#include <iostream> // gibt Ein-und Ausgabe
#include <climits> // gibt Minimal- und Maximalwerte für die Datentypen
int main(){
/*Zuweisen einer Gleitkommazahl zu einer Ganzzahl schneidet die
Nachkommastellen ab
*/
int a = 3.5;
std::cout << "a: " << a << std::endl;
int n1 = SHRT_MAX;
short n2 = SHRT_MAX;
short n3 = n2 + n2; // overflow
int n4 = n1 + n2; //der größere Datentyp gewinnt
std::cout << "n3, n4: "<<n3 << ", " << n4<< std::endl;
unsigned int n5 = 5;
int n6 = -6;
// Bei Kombination von unsigned und signed Typen gewinnt
// der unsigned, der Inhalt der Speicherzelle wird dann
// intern anders interpretiert
std::cout << "n5+n6: "<< n5+6 <<std::endl;
// Bei der Kombination von Gleitkommazahlen und Ganzzahlen gewinnt
// die Gleitkommazahl
double f1 = 0.4;
std::cout << f1 + n6<< std::endl;
int i1 = 18;
int i2 = 16;
//implizieter Typcast, es wird erst Ganzzahlig dividiert
// (Nachkommastellen abgeschnitten) und dann umgewandelt
double dv1 = i1 / i2;
//werden Divisor und Divident erst umgewandelt, ist das Ergebnis die
//erwartete Gleitkommazahl
double dv2 = static_cast<double>(i1) / static_cast<double>(i2);
// Wie weiter oben, erst wird geteilt (und abgeschnitten) und dann umgewandelt
// und dann zugewiesen.
double dv3 = static_cast<double>( i1 / i2 );
std::cout << dv1 << ", " << dv2 << ", " << dv3 << std::endl;
}
+139
View File
@@ -0,0 +1,139 @@
#include <iostream>
#include <vector>
#include <string>
int main() {
// 1.
int eingabe0 = -1;
std::cout << "Aufgabe (1) oder (2)?" << std::endl;
std::cin >> eingabe0;
if (eingabe0 == 1){
int eingabe1 = -1;
std::vector<int> zahlenfolge;
int min = 0;
int max = 0;
while (true) {
std::cout << "Geben Sie eine Zahl der Zahlenfolge ein, mit 0 geben sie MIN und MAX aus, mit 9999 geben sie den Mittelwert aus: " << std::endl;
std::cin >> eingabe1;
if (eingabe1 == 0) {
for (unsigned i = 0; i < zahlenfolge.size(); i++){
if (zahlenfolge[i] < min){
min = zahlenfolge[i];
}
if (zahlenfolge[i] > max){
max = zahlenfolge[i];
}
}
std::cout << "Das Maximum der Zahlenfolge ist: " << max << std::endl;
std::cout << "Das Minimum der Zahlenfolge ist: " << min << std::endl;
break;
}
if (eingabe1 == 9999) {
float summe = 0;
for (unsigned i = 0; i < zahlenfolge.size(); i++){
summe += zahlenfolge[i];
}
double mittelwert = summe / zahlenfolge.size();
std::cout << "Der Mittelwert der Zahlenfolge ist: " << mittelwert << std::endl;
break;
}
zahlenfolge.push_back(eingabe1);
}
}
// 2.
if (eingabe0 == 2){
char eingabe01;
std::cout << "Teilaufgabe (a) oder (b) ?" << std::endl;
std::cin >> eingabe01;
// 2 a)
if (eingabe01 == 'a'){
int eingabe2 = -1;
std::cout << "Geben Sie eine Zahl größer 0 ein, von der die Quersumme berechnet werden soll" << std::endl;
std::cin >> eingabe2;
int eingabe3 = -1;
std::cout << "Soll die Quersumme mit einer while (0) oder for (1) Schleife berechnet werden?" << std::endl;
std::cin >> eingabe3;
if (eingabe3 == 0){
int quersummewhile = 0;
while (eingabe2 > 0){
quersummewhile += eingabe2 % 10;
eingabe2 /= 10;
}
std::cout << "Die Quersumme der Zahl ist: " << quersummewhile << std::endl;
}
if (eingabe3 == 1){
int quersummefor = 0;
for (quersummefor; eingabe2 > 0; eingabe2 /= 10){
quersummefor += eingabe2 % 10;
}
std::cout << "Die Quersumme der Zahl ist: " << quersummefor << std::endl;
}
}
// 2 b)
if (eingabe01 == 'b'){
int n;
std::cout << "Geben Sie eine Zahl größer 0 ein: " << std::endl;
std::cin >> n;
for (int i = 0; i <= n; i++) {
std::string output = "";
if (i % 3 == 0) {
std::cout << "bli" << std::endl;
}
if (i % 5 == 0) {
std::cout << "bla" << std::endl;
}
if (i % 7 == 0) {
std::cout << "blub" << std::endl;
}
}
}
}
}
+56
View File
@@ -0,0 +1,56 @@
# 3. Gültigkeitsbereich und Recherche (5+2+3 Punkte)
## (a) Betrachten Sie den folgenden C++-Code und geben Sie für alle Variablen den Gültigkeitsbereich
an.
```cpp
#include <vector>
int main(){
int v1 = 32;
double i = 7.0;
for(int i= 2; i< 30; i++){
v1 *= i; }
std::vector<std::vector<int>> vec = {
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9, 4 } };
for (i = 0; i<vec.size(); i++){
int v1=20;
for (unsigned int j=0; j<vec[i].size();j++){
v1 = vec[i][j];
}
v1+=1; }
}
```
Die Variable v1 befindet sich in dem Gültigkeitsbereich von main() und auch in dem Gültigkeitsbereich der for-Schleife.
Die Variable i befindet sich in dem Gültigkeitsbereich von main() und in dem Gültigkeitsbereich der ersten for-Schleife. Die Variable i aus dem Gültigkeitsbereich von main() wird für die for-Schleife überschrieben.
Die Variable vec befindet sich in dem Gültigkeitsbereich von main() und in dem Gültigkeitsbereich der zweiten for-Schleife.
Die Variable i aus dem Gültigkeitsbereich der ersten for-Schleife wird für die zweite for-Schleife überschrieben.
Die Variable j befindet sich in dem Gültigkeitsbereich der zweiten for-Schleife.
## (b) Es gibt in C++ noch eine weitere Schleifenart. Recherchieren Sie welche dies ist. Geben Sie die allgemeine Form an und beschreiben Sie die Funktionalität. Denken Sie daran Quellen anzugeben.
Die weitere Schleifenart ist die do-while-Schleife. Die allgemeine Form ist:
```cpp
do {
<Anweisung>;
} while (<Bedingung>);
```
Sie verhält sich wie eine while-Schleife, mit dem Unterschied, dass die Bedingung nach der Ausführung der Anweisung anstatt davor ausgewertet wird. Dadurch wird garantiert, dass die Anweisung mindestens einmal ausgeführt wird, auch wenn die Bedingung niemals erfüllt ist.
[Quelle](https://www.cplusplus.com/doc/tutorial/control/#do_while)
## (c) Finden Sie je einen Anwendungsfall, in dem eine for, while und die weitere Schleife gegenüber den anderen Schleifenarten zu bevorzugen ist.
Die for-Schleife ist zu bevorzugen, wenn die **Anzahl der Durchläufe bekannt** ist.
Die while-Schleife ist zu bevorzugen, wenn die **Anzahl der Durchläufe nicht bekannt** ist.
Die do-while-Schleife ist zu bevorzugen, wenn die **Anweisung mindestens einmal ausgeführt werden soll**.
+43
View File
@@ -0,0 +1,43 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu vector
*/
#include <iostream>
#include <vector>
int main(){
std::vector<int> zahlen = {10,4,20,24,7,34,1,55,34,17,4};
//vector mit 10 Boolean einträgen, die alle 0 bzw false sind
std::vector<bool> wahrheitswerte(10);
//vector mit 5 Einträgen, alle sind "Mo"
std::vector<std::string> montage(5,"Mo");
std::cout << zahlen[3] << std::endl;
zahlen[3] = 100;
std::cout << zahlen[3] << std::endl;
std::cout << zahlen.size() << std::endl;
zahlen.push_back(1234);
std::cout << zahlen.size() << std::endl;
//ACHTUNG! Kein Fehler, wenn wir auf Daten außerhalb des vector zugreifen
std::cout << zahlen[zahlen.size()] << std::endl;
//Wird auf einen Index größer oder gleich size() des vector
// zugegriffen gibt es undefiniertes Verhalten.
// Es kann ein Wert zurückgegeben werde, oder ein Speicherzugriffehler
// ausgegeben werden.
// Wird ein Wert außerhalb der Grenzen zugewiesen, wird die Größe
// des vector nicht angepasst
std::cout << zahlen[100000] << std::endl;
std::cout << zahlen.size() << std::endl;
}
+30
View File
@@ -0,0 +1,30 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 28.04.2023
Katharina Klost
Beispiele zu vector und schleifen
*/
#include <iostream>
#include <vector>
int main(){
std::vector<int> zahlen = {10,4,20,24,7,34,1,55,34,17,4};
int sum = 0;
//Schleife läuft durch alle Indizes des vector und summiert die Einträge auf
for (unsigned int i = 0; i<zahlen.size(); i++){
sum += zahlen[i];
}
std::cout << "Summe des vector: " << sum << std::endl;
//Verschachtelte Schleife geht durch alle Paare i,j mit i<j und i,j < zahlen.size()
std::cout << "Alle paare von Werten: " << std::endl;
for(unsigned i = 0; i< zahlen.size(); i++){
for(unsigned j = i+1; j<zahlen.size(); j++){
std::cout << "(" << zahlen[i] << ", " << zahlen[j] <<")" << std::endl;
}
}
}
+44
View File
@@ -0,0 +1,44 @@
#include <iostream>
#include <vector>
#include <string>
int main() {
int vector_size;
std::vector<int> vec;
std::cout << "Bitte geben Sie die Größe des Vektors an: ";
std::cin >> vector_size;
std::cout << "Geben Sie die Werte des Vektors ein:" << std::endl;
for (int i = 0; i < vector_size; ++i) {
int input;
std::cin >> input;
vec.push_back(input);
}
int count_1 = 0;
int count_3 = 0;
bool found = false;
for (int i = 0; i < vector_size; ++i) {
if (vec[i] == 1) count_1++;
if (vec[i] == 3) count_3++;
if (i + 3 < vector_size) {
if (vec[i] == 1 && vec[i + 1] == 9 && vec[i + 2] == 8 && vec[i + 3] == 9) {
found = true;
}
}
}
std::cout << "Anzahl der 1en im Vektor: " << count_1 << std::endl;
std::cout << "Anzahl der 3en im Vektor: " << count_3 << std::endl;
if (found) {
std::cout << "Das Album \"1989\" von Taylor Swift ist ein sehr gutes Album." << std::endl;
} else {
std::cout << "\"1989\" kommt nicht vor." << std::endl;
}
return 0;
}
+81
View File
@@ -0,0 +1,81 @@
#include <iostream>
#include <string>
#include <vector>
enum Abteilung {
IT,
FINANZ,
MARKETING,
PERSONAL,
PRODUKTION
};
struct angestellter {
std::string name;
int personalnummer;
Abteilung abteilung;
double gehalt;
};
angestellter angestellter_eingeben();
std::vector<angestellter> abteilung_check(std::vector<angestellter> vec, Abteilung abt);
int main() {
angestellter beispielAngestellter = angestellter_eingeben();
std::cout << std::endl << "Angestellter:" << std::endl;
std::cout << "Name: " << beispielAngestellter.name << std::endl;
std::cout << "Personalnummer: " << beispielAngestellter.personalnummer << std::endl;
std::cout << "Abteilung: " << beispielAngestellter.abteilung << std::endl;
std::cout << "Gehalt: " << beispielAngestellter.gehalt << std::endl;
angestellter kollege1 = {"Hans Wurst", 678, PRODUKTION, 3550.5};
angestellter kollege2 = {"Scrooge McDough", 022, FINANZ, 4702};
angestellter kollege3 = {"Ninja Scripter", 337, IT, 2500};
angestellter kollege4 = {"Snake Oil", 888, MARKETING, 1800};
angestellter kollege5 = {"Overlord", 007, PERSONAL, 5600};
std::vector<angestellter> belegschaft = {kollege1, kollege2, kollege3, kollege4, kollege5};
std::vector<angestellter> filtered = abteilung_check(belegschaft, beispielAngestellter.abteilung);
// Display filtered employees or a message if no matching employees are found.
if (!filtered.empty()) {
for (const auto& ang : filtered) {
std::cout << "Filtered employee: " << ang.name << std::endl;
}
} else {
std::cout << "No matching employees found." << std::endl;
}
return 0;
}
std::vector<angestellter> abteilung_check(std::vector<angestellter> vec, Abteilung abt) {
std::vector<angestellter> division;
for (const auto &ang : vec) {
if (ang.abteilung == abt) {
division.push_back(ang);
}
}
return division;
}
angestellter angestellter_eingeben() {
angestellter neuerAngestellter;
int abteilungWahl;
std::cout << "Name: ";
std::getline(std::cin, neuerAngestellter.name);
std::cout << "Personalnummer: ";
std::cin >> neuerAngestellter.personalnummer;
std::cout << "Abteilung (0: IT, 1: FINANZ, 2: MARKETING, 3: PERSONAL, 4: PRODUKTION): ";
std::cin >> abteilungWahl;
neuerAngestellter.abteilung = static_cast<Abteilung>(abteilungWahl);
std::cout << "Gehalt: ";
std::cin >> neuerAngestellter.gehalt;
std::cin.ignore();
return neuerAngestellter;
}
+62
View File
@@ -0,0 +1,62 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 03.05.2023
Katharina Klost
Weiteres Beispiel zur Rekursion
*/
#include <iostream>
/*
Voraussetzung: n ist größer als 0
Ergebnis: Wenn Collatz mit n/2 terminiert ist true geliefert
Effekt: keiner
*/
bool c1(unsigned);
/*
Voraussetzung: n ist größer als 0
Ergebnis: Wenn Collatz mit 3*n+1 terminiert ist true geliefert
Effekt: keiner
*/
bool c2(unsigned);
/*
Voraussetzung: n ist größer als 0
Ergebnis: true, wenn die Collatzfolge von n aus terminiert
Effekt: gibt aktuellen Wert n aus
*/
bool collatz(unsigned);
bool c1(unsigned n){
return collatz(n/2);
}
bool collatz(unsigned n){
std::cout << n << std::endl;
if (n==1){
return true;
}
// !(n%2) ist true, wenn n gerade ist. Dies kann wie folgt gesehen werden:
// für gerade n ist n%2 == 0. Das Anwenden von ! interpretiert die 0 dann als
// false, dieses wird negiert und damit wird der Ausdruck zu true ausgewertet.
// Etwas länger aber evtl besser zu lesen wär (n%2 == 0).
if (!(n%2)){
return c1(n);
}else{ // n ungerade
return c2(n);
}
}
bool c2(unsigned n){
return collatz(3*n+1);
}
int main(){
int n;
std::cout << "Gebe eine Zahl ein: ";
std::cin >> n;
collatz(n);
std::cout << "terminated" << std::endl;
}
+65
View File
@@ -0,0 +1,65 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 05.05.2023
Katharina Klost
Verschiedene Umsetzungen des euklidischen Algorithmus
*/
#include <iostream>
/*
Voraussetzung: keine
Effekt: Es werden Informationen zum Ablauf ausgegeben
Ergebnis: Der größte gemeinsame Teiler von a und b ist geliefert
*/
// Rekursive Umsetzung
unsigned euclid_rec(unsigned a, unsigned b);
//optimierte rekursive Umsetzung
unsigned euclid_rec_opt(unsigned a, unsigned b);
//iterative Umsetzung
unsigned euclid_it(unsigned a, unsigned b);
unsigned euclid_rec(unsigned a, unsigned b){
std::cout << "ggT(" << a << ", " << b << ")" << std::endl;
if (b>a){
return euclid_rec(b,a);
}
if(b==0){
return a;
}
return euclid_rec(a-b, b);
}
// statt immer wieder a-b zu rechnen, bis das Ergebnis kleiner als b ist,
// kann direkt modulu verwendet werden
unsigned euclid_rec_opt(unsigned a, unsigned b){
std::cout << "ggTopt(" << a<< ", " << b << ")" << std::endl;
if(b==0){
return a;
}
return euclid_rec_opt(b, a%b);
}
//iterative Umsetzung des optimierten euklidischen Algorithmus
unsigned euclid_it(unsigned a, unsigned b){
unsigned newb;
while(b!=0){
newb = a%b;
a=b;
b = newb;
}
return a;
}
int main(){
int a= 12;
int b= 136;
std::cout << "===Rekursiv===" << std::endl;
int ggT= euclid_rec(a , b);
std::cout << "Der ggT von " << a << " und " << b << " ist " << ggT <<std::endl;
std::cout << "===Rekursiv Optimiert ===" << std::endl;
ggT= euclid_rec_opt(a , b);
std::cout << "Der ggT von " << a << " und " << b << " ist " << ggT <<std::endl;
}
+70
View File
@@ -0,0 +1,70 @@
/*
Arbeiten mit Dateien in C++:
- Datei öffnen
- Datei lesen
- Datei schließen
Quelle: https://cplusplus.com/reference/fstream/
histogram() ist eine Funktion, die ein Histogramm der Buchstaben in einem String erstellt.
- Vorraussetzungen: str ist ein String
- Ergebnis: hist ist ein Vektor, der die Häufigkeit der Buchstaben in str enthält
- Seiteneffekte: keine
*/
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
std::vector<int> histogram(std::string str);
int main() {
std::ifstream inputFile("moby.txt");
std::string content, line;
while (std::getline(inputFile, line)) {
content += line;
}
std::vector<int> hist = histogram(content);
for (unsigned i = 0; i < 26; i++) {
if (hist[i] > 0) {
std::cout << static_cast<char>('a' + i) << ": " << hist[i] << ", ";
}
}
for (unsigned i = 26; i < hist.size(); i++) { // Loop for uppercase letters
if (hist[i] > 0) {
std::cout << static_cast<char>('A' + i - 26) << ": " << hist[i] << ", ";
}
}
std::cout << std::endl;
inputFile.close();
return 0;
}
std::vector<int> histogram(std::string str) {
std::vector<int> hist(52, 0);
for (unsigned i = 0; i < str.length(); i++) {
if (str[i] >= 'a' && str[i] <= 'z') {
hist[str[i] - 'a']++;
}
else if (str[i] >= 'A' && str[i] <= 'Z') {
hist[str[i] - 'A' + 26]++;
}
}
return hist;
}
+91
View File
@@ -0,0 +1,91 @@
#include <iostream>
#include <string>
#include <vector>
// Enum für die verschiedenen Menüpunkte
enum MenuType {
Vorspeise,
Hauptgang,
Dessert
};
// Struct für die Details eines Menüpunkts
struct MenuItem {
MenuType type;
std::string name;
std::string description;
double price;
};
void showMenu(const std::vector<MenuItem>& menu) {
for (size_t i = 0; i < menu.size(); ++i) {
std::string type;
switch (menu[i].type) {
case Vorspeise:
type = "Vorspeise";
break;
case Hauptgang:
type = "Hauptgang";
break;
case Dessert:
type = "Dessert";
break;
}
std::cout << type << ": " << menu[i].name << " - " << menu[i].description << " (€" << menu[i].price << ")\n";
}
}
MenuItem searchMenuItem(const std::vector<MenuItem>& menu, const std::string& name) {
for (size_t i = 0; i < menu.size(); ++i) {
if (menu[i].name == name) {
return menu[i];
}
}
return MenuItem{static_cast<MenuType>(-1), "", "", -1.0};
}
int main() {
// Erstellen des Restaurantmenüs
std::vector<MenuItem> menu = {
{Vorspeise, "Bruschetta", "Geröstetes Brot mit Tomaten, Knoblauch und Basilikum", 5.0},
{Hauptgang, "Spaghetti Bolognese", "Spaghetti mit würziger Hackfleisch-Tomatensauce", 10.0},
{Dessert, "Tiramisu", "Italienisches Dessert aus Löffelbiskuit, Mascarpone und Kaffee", 6.0}
};
std::cout << "Willkommen im Restaurant!\n";
std::cout << "Möchten Sie das Menü anzeigen oder nach einem bestimmten Menüpunkt suchen?\n";
std::cout << "1. Menü anzeigen\n";
std::cout << "2. Nach Menüpunkt suchen\n";
std::cout << "3. Beenden\n";
int option;
std::cin >> option;
while (option != 3) {
if (option == 1) {
showMenu(menu);
} else if (option == 2) {
std::string name;
std::cout << "Geben Sie den Namen des Menüpunkts ein, nach dem Sie suchen möchten: ";
std::cin.ignore();
std::getline(std::cin, name);
MenuItem item = searchMenuItem(menu, name);
if (item.price != -1.0) {
std::cout << item.name << " - " << item.description << " (€" << item.price << ")\n";
} else {
std::cout << "Menüpunkt nicht gefunden.\n";
}
} else {
std::cout << "Ungültige Option.\n";
}
std::cout << "\nMöchten Sie das Menü anzeigen oder nach einem bestimmten Menüpunkt suchen?\n";
std::cout << "1. Menü anzeigen\n";
std::cout << "2. Nach Menüpunkt suchen\n";
std::cout << "3. Beenden\n";
std::cin >> option;
}
std::cout << "Vielen Dank für Ihren Besuch! Auf Wiedersehen!\n";
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 03.05.2023
Katharina Klost
Beispiele zu rekursion und iteration
*/
#include <iostream>
/*
Voraussetzung: keine
Ergebnis: Die Fakultät von n ist geliefert
Effekt: keiner
Rekursive Implementierung
*/
unsigned fak_rek(unsigned n);
/*
Voraussetzung: keine
Ergebnis: Die Fakultät von n ist geliefert
Effekt: keiner
Iterative Implementierung
*/
unsigned fak_iter(unsigned n);
//Da hier direkte rekursion verwendet wird, gibt es keine Optimierung.
// Bei Endrekursion und Verwendung des -O3 flags wird die Endrekursion optimiert
unsigned fak_rek(unsigned n){
if (n==0){
return 1;
}
return(fak_rek(n-1)*n);
}
unsigned fak_iter(unsigned n){
unsigned res = 1;
for (unsigned i = 1; i<=n; i++){
res*=i;
}
return res;
}
int main(){
}
+43
View File
@@ -0,0 +1,43 @@
#include <iostream>
#include <string>
/*
- Voraussetzungen: str ist ein String, charToReplace und replacementChar sind Zeichen
- Ergebnis: str ist ein String, in dem alle Vorkommen von charToReplace durch replacementChar ersetzt wurden
- Seiteneffekte: keine
Der Rückgabetyp ist string, weil wir einen neuen String zurückgeben wollen, in dem alle Vorkommen von charToReplace durch replacementChar ersetzt wurden.
Die Parameter sind char, weil wir nur ein Zeichen ersetzen wollen.
*/
std::string repl(std::string str, char charToReplace, char replacementChar);
int main() {
std::cout << "Bitte gebe einen String ein bei dem du die Zeichen ersetzen willst: ";
std::string s;
std::getline(std::cin, s);
std::cout << "Bitte gebe das Zeichen ein, welches zu ersetzen möchtest: ";
char a;
std::cin >> a;
std::cout << "Bitte gebe das Zeichen ein, durch welches du ersetzen möchtest: ";
char b;
std::cin >> b;
std::cout << "Dein neuer String ist: " << repl(s, a, b) << std::endl;
}
std::string repl(std::string str, char charToReplace, char replacementChar) {
for (unsigned i = 0; i < str.length(); i++) {
if (str[i] == charToReplace) {
str[i] = replacementChar;
}
}
return str;
}
+35
View File
@@ -0,0 +1,35 @@
#include <iostream>
#include <vector>
std::vector<int> reverseSortVector(std::vector<int> vec);
int main() {
int input = -1;
std::vector<int> vec;
std::vector<int> vecnew;
std::cout << "Geben Sie eine Zahlenfolge ein, mit 0 beenden Sie die Eingabe: " << std::endl;
while (input != 0) {
std::cin >> input;
if (input != 0) {
vec.push_back(input);
}
}
vecnew = reverseSortVector(vec);
std::cout << "Die Zahlenfolge in umgekehrter Reihenfolge: " << std::endl;
for (unsigned i = 0; i < vecnew.size(); i++) {
std::cout << vecnew[i] << std::endl;
}
}
std::vector<int> reverseSortVector(std::vector<int> vec) {
std::vector<int> vecnew;
for (unsigned i = vec.size() - 1; i >= 0; i--) {
vecnew.push_back(vec[i]);
}
return vecnew;
}
+47
View File
@@ -0,0 +1,47 @@
#include <iostream>
#include <vector>
#include <algorithm>
/*
Voraussetzung: keine
Effekt: Keiner
Ergebnis: Eine größtmögliche Auswahl von Elementen, deren Summe von Gewichten unter bound ist,
ist geliefert.
*/
std::vector<unsigned> rucksack_einfach(unsigned bound, std::vector<unsigned> gewichte);
/*
Voraussetzung: Der vector hat mindestens ein Element
Effekt: Die Elementen von vec im geschlossenen Intervall [start, end] werden ausgegeben
Ergebnis: keins
*/
void gebe_aus(std::vector<unsigned>& vec, unsigned start, unsigned end);
std::vector<unsigned> rucksack_einfach(unsigned bound, std::vector<unsigned> gewichte){
std::vector<unsigned> rucksack_inhalt ={};
std::sort(gewichte.begin(), gewichte.end());
for(unsigned i = 0; i<gewichte.size() && bound >= gewichte[i]; i++){
bound = bound - gewichte[i];
rucksack_inhalt.push_back(gewichte[i]);
}
return rucksack_inhalt;
}
void gebe_aus(std::vector<unsigned>& vec, unsigned start, unsigned end){
if(end>vec.size() || start >= end){
std::cout << vec.size() << " " << end << std::endl;
return;
}
std::cout << vec[start];
for(unsigned k=start+1; k<end; k++){
std::cout << ", " << vec[k];
}
std::cout << std::endl;
}
int main(){
std::vector<unsigned> gewichte = {3,7,4,17,5,6};
std::vector<unsigned> einfach_erg = rucksack_einfach(12, gewichte);
gebe_aus(einfach_erg, 0, einfach_erg.size());
}
+33
View File
@@ -0,0 +1,33 @@
#include <iostream>
#include <string>
/*
- Voraussetzungen: str ist ein String
- Ergebnis: str ist ein String, der nur aus Großbuchstaben besteht
- Seiteneffekte: keine
Der Rückgabetyp ist string, weil wir einen neuen String zurückgeben wollen, der nur aus Großbuchstaben besteht.
Der Parameter ist string, weil wir einen String übergeben wollen, bei dem wir die Buchstaben in Großbuchstaben umwandeln wollen.
*/
std::string upper(std::string str);
int main() {
std::cout << "Bitte gebe einen String ein der groß geschrieben werden soll: ";
std::string s;
std::getline(std::cin, s);
std::cout << "Hier ist dein neuer String: " << upper(s) << std::endl;
}
std::string upper(std::string str) {
for (unsigned i = 0; i < str.length(); i++) {
if (str[i] >= 'a' && str[i] <= 'z') {
str[i] = str[i] - 32;
}
}
return str;
}
+34
View File
@@ -0,0 +1,34 @@
// g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/binarysearch.cpp -o ./bin/binarysearch
#include <iostream>
#include <vector>
int binarySearch(int query, std::vector<int> &elems)
{
unsigned low = 0;
unsigned high = elems.size() - 1;
unsigned index = (low + high) / 2;
while (low < high)
{
if (query == elems[index])
{
return elems[index];
}
if (query < elems[index])
{
high = index - 1;
}
else
{
low = index + 1;
}
index = (low + high) / 2;
}
return elems[index];
}
int main()
{
std::vector<int> searchtest = {1, 4, 6, 12, 19, 22, 34, 36, 100};
std::cout << "Binärsuche: " << binarySearch(36, searchtest) << std::endl;
std::cout << "Binärsuche: " << binarySearch(1000, searchtest) << std::endl;
std::cout << "Binärsuche: " << binarySearch(0, searchtest) << std::endl;
std::cout << "Binärsuche: " << binarySearch(5, searchtest) << std::endl;
}
+53
View File
@@ -0,0 +1,53 @@
// g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/bubblesort.cpp -o ./bin/bubblesort
#include <iostream>
#include <vector>
void bubbleSort(std::vector<int>& arr, bool ascending) {
int n = arr.size();
for (int i = 0; i < n - 1; ++i) {
bool swapped = false;
for (int j = 0; j < n - i - 1; ++j) {
if ((arr[j] > arr[j + 1] && ascending) || (arr[j] < arr[j + 1] && !ascending)) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
if (!swapped) {
break;
}
}
}
int main() {
int arr[] = {30, 10, 25, 100, 5, 16};
int n = sizeof(arr) / sizeof(arr[0]);
std::vector<int> numbers(arr, arr + n);
bool ascending = true;
// Ascending sort
bubbleSort(numbers, ascending);
std::cout << "Sorted list (ascending): ";
for (unsigned i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
// Descending sort
ascending = false;
bubbleSort(numbers, ascending);
std::cout << "Sorted list (descending): ";
for (unsigned i = 0; i < numbers.size(); ++i) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
# 1. Gierige Algorithmen
## a)
Gegeben:
Eine Liste L von n Dateien. Jede Datei i hat eine Größe s_i und eine Zugriffswahrscheinlichkeit p_i. Da die Wahrscheinlichkeit für alle Dateien gleich ist, ist p_i = 1/n für alle i.
Ein Bandspeicher.
Gesucht:
Eine optimale Reihenfolge der Dateien auf dem Band, um die erwartete Zugriffszeit zu minimieren. Die Zugriffszeit auf eine Datei i ist gleich der Summe der Größen aller Dateien, die vor i auf dem Band stehen.
## b)
Eine einfache gierige Strategie zur Lösung dieses Problems wäre, die Dateien in aufsteigender Reihenfolge ihrer Größe auf das Band zu schreiben. Der gierige Algorithmus sortiert die Dateien nach ihrer Größe und schreibt sie in dieser Reihenfolge auf das Band.
1. Eingabe: Liste L von Dateien mit Größen s_i
2. Sortiere L in aufsteigender Reihenfolge nach s_i
3. Schreibe die Dateien in der sortierten Reihenfolge auf das Band
## c)
Angenommen, wir haben eine optimale Reihenfolge O und die gierige Reihenfolge G, und O unterscheidet sich von G. Wir können immer zwei benachbarte Dateien in O finden, die in umgekehrter Reihenfolge im Vergleich zu G angeordnet sind, d.h. eine größere Datei steht vor einer kleineren.
Wir nehmen diese beiden Dateien und tauschen sie. Dieser Tausch führt zu einer Reduzierung oder zumindest zu keiner Änderung der gesamten Zugriffszeit, da jede Zugriffsoperation auf die größere Datei jetzt weniger kostet. Dieser Tausch verletzt nicht die Ordnung der restlichen Dateien.
Indem wir dieses Verfahren wiederholen, können wir die Reihenfolge O allmählich in die Reihenfolge G umwandeln, ohne dass die Gesamtzugriffszeit steigt. Daher muss G mindestens genauso gut sein wie jede andere Reihenfolge, einschließlich O. Daher ist G optimal.
## d)
d4,d1,d3,d6,d2,d5
+52
View File
@@ -0,0 +1,52 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 12.05.2023
Katharina Klost
Hilfsfunktionen zum Sortieren, nur zusammen mit der sorting.cpp zu compilieren
*/
#include <iostream>
#include <vector>
#include "hilfsfunktionen.hpp"
namespace hf{
void gebe_aus(std::vector<int>& vec, unsigned start, unsigned end){
if(end>vec.size()){
std::cout << vec.size() << " " << end << std::endl;
return;
}
std::cout << vec[start];
for(unsigned k=start+1; k<end; k++){
std::cout << ", " << vec[k];
}
std::cout << std::endl;
}
void swap(std::vector<int>& elems, unsigned i, unsigned j){
if (i>=elems.size() || j >= elems.size()){
return;
}
int tmp = elems[i];
elems[i] = elems[j];
elems[j] = tmp;
}
unsigned minimumIndex(std::vector<int>& elems, unsigned start, unsigned end){
unsigned min = start;
for(unsigned i = start; i<end; i++){
if(elems[i] < elems[min]){
min = i;
}
}
return min;
}
std::vector<int> copy(std::vector<int>& elems, unsigned start, unsigned end){
std::vector<int> returnVec;
for(unsigned i= start; i<end; i++){
returnVec.push_back(elems[i]);
}
return returnVec;
}
}
+57
View File
@@ -0,0 +1,57 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 12.05.2023
Katharina Klost
Headerdatei zu den Hilfsfunktionen zum Sortieren
*/
#include <vector>
// #include "telefonbuch.hpp"
namespace hf{
/*
Voraussetzung: 0 <= start <= end < vec.elems()
Ergebnis: Keins
Effekt: Gibt die Elemente des Vektors im Intervall [start,end) aus
*/
void gebe_aus(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: 0<=start<=end < vec.elems()
Ergebnis: Ein vector, mit alle Elemente im Intervall [start, end)
Effekt: Keiner
*/
std::vector<int> copy(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: 0 <= i,j <= elems.size()
Ergebnis: Keins
Effekt: Tauscht die Einträge and Position i und j
*/
void swap(std::vector<int>& elems, unsigned i, unsigned j);
/*
Voraussetzung: Es gilt 0 <= start <= end < elems.size()
Effekt: Keiner
Ergebni: Index des kleinesten Elements zwischen start und end
*/
unsigned minimumIndex(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: left < right
Ergebnis: Eine Zufallszahl zwischen left(inklusive) und right (exklusive)
Effekt: Keiner
*/
int zufall(int left, int right);
std::vector<int> sortiertes_array(unsigned size, bool aufsteigend);
std::vector<int> zufalls_array(unsigned size);
void test();
}
+39
View File
@@ -0,0 +1,39 @@
/*
g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/hilfsfunktionen.cpp ./w4/insertionsort.cpp -o ./bin/insertionsort
Insertion Sort funktioniert, indem es durch das Array läuft und jedes Element nach links verschiebt,
bis es das erste Element findet, das kleiner ist. Dieser Prozess wird für jedes Element im Array wiederholt,
bis das gesamte Array sortiert ist. Im Hauptprogramm (main) wird der Insertion Sort Algorithmus auf ein Testarray angewendet
und das Array vor und nach der Sortierung ausgegeben.
*/
#include <vector>
#include "hilfsfunktionen.hpp"
void insertionSort(std::vector<int>& elems){
// Der Schlüssel ist das Element, das wir gerade sortieren
int key=elems[0];
unsigned shiftindex;
// Beginne mit dem zweiten Element (Index 1)
for(unsigned i = 1; i<elems.size();i++){
// Aktualisiere den Schlüssel auf das aktuelle Element
key=elems[i];
// Verschiebe jedes Element, das größer als der Schlüssel ist, um eine Position nach rechts
for(shiftindex=i; shiftindex>0&&elems[shiftindex-1]>=key;shiftindex--){
elems[shiftindex] = elems[shiftindex-1];
}
// Füge den Schlüssel an der richtigen Position ein
elems[shiftindex] = key;
}
}
int main(){
std::vector<int> test = {1,4,2,67,3,76,13,8,10,22,6,9,5,11,32,0};
hf::gebe_aus(test, 0, test.size());
insertionSort(test);
hf::gebe_aus(test, 0, test.size());
}
+58
View File
@@ -0,0 +1,58 @@
/*
g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/hilfsfunktionen.cpp ./w4/mergesort.cpp -o ./bin/mergesort
Dieser Algorithmus verwendet den Divide-and-Conquer-Ansatz, um das Array zu sortieren. Zuerst wird das Array in zwei Hälften geteilt.
Diese Hälften werden dann rekursiv sortiert (mit mergeSortHelp). Danach werden die beiden sortierten Hälften mit der merge Funktion
zusammengeführt. Dieser Prozess wird fortgesetzt, bis das gesamte Array sortiert ist. Im Hauptprogramm (main) wird der Merge Sort
Algorithmus auf ein Testarray angewendet und das Array vor und nach der Sortierung ausgegeben.
*/
#include <vector>
#include "hilfsfunktionen.hpp"
void merge(std::vector<int>& elems, unsigned begin, unsigned mid, unsigned end){
// Kopiere die erste und zweite Hälfte des zu sortierenden Bereichs in neue Vektoren
std::vector<int> firstHalf = hf::copy(elems, begin, mid);
std::vector<int> secondHalf = hf::copy(elems, mid, end);
// Initialisiere Zähler für die beiden Hälften
unsigned i = 0;
unsigned j = 0;
// Iteriere durch den zu sortierenden Bereich
while(begin + i + j < end){
// Wenn alle Elemente aus der zweiten Hälfte kopiert wurden oder das aktuelle Element der ersten Hälfte kleiner ist als das der zweiten Hälfte
if ( j >= secondHalf.size() || ((i<firstHalf.size()) && (firstHalf[i]<secondHalf[j]))){
// Kopiere das aktuelle Element der ersten Hälfte in den ursprünglichen Vektor
elems[begin+i+j] = firstHalf[i];
i++;
}else{
// Kopiere das aktuelle Element der zweiten Hälfte in den ursprünglichen Vektor
elems[begin+i+j] = secondHalf[j];
j++;
}
}
}
void mergeSortHelp(std::vector<int>& elems, unsigned start, unsigned end){
if(start==end||start==end-1){
return;
}
unsigned mid = (start+end)/2;
mergeSortHelp(elems, start, mid);
mergeSortHelp(elems, mid, end);
merge(elems, start, mid, end);
}
void mergeSort(std::vector<int>& elems){
mergeSortHelp(elems, 0, elems.size());
}
int main(){
std::vector<int> test = {1,4,2,67,3,76,13,8,10,22,6,9,5,11,32,0};
hf::gebe_aus(test, 0, test.size());
mergeSort(test);
hf::gebe_aus(test, 0, test.size());
}
+68
View File
@@ -0,0 +1,68 @@
// g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/rucksack2.cpp -o ./bin/rucksack2
#include <iostream>
#include <vector>
struct Gegenstand {
int gewicht;
int wert;
Gegenstand(int g, int w) : gewicht(g), wert(w) {}
};
int rucksackWert(const std::vector<Gegenstand>& gegenstaende, int kapazitaet, int index) {
if (index < 0 || kapazitaet == 0) {
return 0;
}
if (gegenstaende[index].gewicht > kapazitaet) {
return rucksackWert(gegenstaende, kapazitaet, index - 1);
} else {
int wert1 = rucksackWert(gegenstaende, kapazitaet, index - 1);
int wert2 = gegenstaende[index].wert + rucksackWert(gegenstaende, kapazitaet - gegenstaende[index].gewicht, index - 1);
return (wert1 > wert2) ? wert1 : wert2;
}
}
std::vector<Gegenstand> rucksackGegenstaende(const std::vector<Gegenstand>& gegenstaende, int kapazitaet, int index) {
if (index < 0 || kapazitaet == 0) {
return std::vector<Gegenstand>();
}
if (gegenstaende[index].gewicht > kapazitaet) {
return rucksackGegenstaende(gegenstaende, kapazitaet, index - 1);
} else {
int wert1 = rucksackWert(gegenstaende, kapazitaet, index - 1);
int wert2 = gegenstaende[index].wert + rucksackWert(gegenstaende, kapazitaet - gegenstaende[index].gewicht, index - 1);
if (wert1 > wert2) {
return rucksackGegenstaende(gegenstaende, kapazitaet, index - 1);
} else {
std::vector<Gegenstand> ausgewaehlteGegenstaende = rucksackGegenstaende(gegenstaende, kapazitaet - gegenstaende[index].gewicht, index - 1);
ausgewaehlteGegenstaende.push_back(gegenstaende[index]);
return ausgewaehlteGegenstaende;
}
}
}
int main() {
std::vector<Gegenstand> gegenstaende;
gegenstaende.push_back(Gegenstand(2, 10));
gegenstaende.push_back(Gegenstand(3, 15));
gegenstaende.push_back(Gegenstand(5, 20));
gegenstaende.push_back(Gegenstand(7, 25));
int kapazitaet = 10;
int optimalerWert = rucksackWert(gegenstaende, kapazitaet, gegenstaende.size() - 1);
std::cout << "Optimaler Wert: " << optimalerWert << std::endl;
std::vector<Gegenstand> optimaleGegenstaende = rucksackGegenstaende(gegenstaende, kapazitaet, gegenstaende.size() - 1);
std::cout << "Optimale Gegenstände: ";
for (const auto& gegenstand : optimaleGegenstaende) {
std::cout << "(" << gegenstand.gewicht << ", " << gegenstand.wert << ") ";
}
std::cout << std::endl;
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
/*
g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w4/hilfsfunktionen.cpp ./w4/selectionsort.cpp -o ./bin/selectionsort
Dieser Algorithmus durchläuft das Array und findet das kleinste Element, dann tauscht es dieses Element mit dem ersten Element des Arrays.
Danach durchläuft es den restlichen Teil des Arrays, findet das kleinste Element und tauscht es mit dem zweiten Element des Arrays.
Dieser Prozess wird so lange wiederholt, bis das gesamte Array sortiert ist. Im Hauptprogramm (main) wird der Selection Sort Algorithmus auf
ein Testarray angewendet und das Array vor und nach der Sortierung ausgegeben.
*/
#include <vector>
#include "hilfsfunktionen.hpp"
void selectionSort(std::vector<int>& elems){
for(unsigned i = 0; i < elems.size();i++){
hf::swap(elems, i, hf::minimumIndex(elems, i, elems.size()));
}
}
int main(){
std::vector<int> test = {1,4,2,67,3,76,13,8,10,22,6,9,5,11,32,0};
hf::gebe_aus(test, 0, test.size());
selectionSort(test);
hf::gebe_aus(test, 0, test.size());
}
+41
View File
@@ -0,0 +1,41 @@
# 3. Sortieralgorithmen
## a)
Bubblesort ist ein einfacher Sortieralgorithmus, der wiederholt **benachbarte Elemente in einer Liste vergleicht** und sie bei Bedarf vertauscht, bis die gesamte Liste sortiert ist. Der Algorithmus erhält seinen Namen aufgrund der Art und Weise, wie die **größten (oder kleinsten) Elemente "nach oben blubbern"**, ähnlich wie Blasen in einem aufsteigenden Strom.
Der Ablauf des Bubblesort-Algorithmus kann wie folgt beschrieben werden:
1. Beginne mit einer ungeordneten Liste von Elementen.
2. Vergleiche jeweils benachbarte Elemente der Liste. Wenn das linke Element größer (oder kleiner, abhängig von der Sortierreihenfolge) ist als das rechte Element, vertausche sie.
3. Wiederhole diesen Schritt für jedes Paar benachbarter Elemente in der Liste, bis das größte (oder kleinste) Element an die letzte Position der Liste "blubbert".
4. Reduziere die Größe des zu sortierenden Bereichs um 1 und wiederhole Schritt 2 und 3, bis die Liste vollständig sortiert ist.
Der Bubblesort-Algorithmus hat eine Zeitkomplexität von $O(n^2)$, was bedeutet, dass die Anzahl der Vergleiche und Vertauschungen quadratisch mit der Größe der Liste wächst. Es ist daher nicht effizient für große Listen.
Quellen:
- GeeksforGeeks. "Bubble Sort." https://www.geeksforgeeks.org/bubble-sort/
- Wikipedia. "Bubble sort." https://en.wikipedia.org/wiki/Bubble_sort
## c)
Bogosort, auch bekannt als "Monkey Sort" oder "Random Sort", ist ein extrem **ineffizienter Sortieralgorithmus**, der auf dem **Zufall** basiert. Der Algorithmus folgt einem einfachen Prinzip: Überprüfe, ob die gegebene Liste sortiert ist. Wenn nicht, mische die Elemente zufällig und überprüfe erneut. Dieser Vorgang wird so lange wiederholt, bis die Liste tatsächlich sortiert ist.
1. Der Ablauf des Bogosort-Algorithmus kann wie folgt beschrieben werden:
2. Überprüfe, ob die Liste sortiert ist. Wenn ja, beende den Algorithmus.
3. Falls die Liste nicht sortiert ist, mische die Elemente zufällig.
4. Überprüfe erneut, ob die Liste sortiert ist. Wenn nicht, gehe zu Schritt 2.
Der Bogosort-Algorithmus hat keine feste Zeitkomplexität. In der schlechtesten Fallannahme kann es jedoch eine unendliche Anzahl von Mischungen erfordern, um eine sortierte Liste zu erhalten. Das bedeutet, dass die Laufzeit des Algorithmus im schlimmsten Fall unbegrenzt ist.
Aufgrund dieser extremen Ineffizienz wird der Bogosort-Algorithmus nicht für die praktische Sortierung verwendet.
Quellen:
- GeeksforGeeks. "Bogosort." https://www.geeksforgeeks.org/bogosort-permutation-sort/
- Wikipedia. "Bogosort." https://de.wikipedia.org/wiki/Bogosort
Warum sollte man den Algorithmus nicht mit langen Listen testen?
Da der Bogosort-Algorithmus zufällige Mischungen verwendet, um die Sortierung zu erreichen, kann die Laufzeit des Algorithmus sehr lange dauern. Mit zunehmender Größe der Liste steigt die Anzahl der möglichen Permutationen exponentiell an. Das bedeutet, dass die Wahrscheinlichkeit, dass der Algorithmus eine sortierte Liste findet, mit zunehmender Listenlänge sehr gering wird. Für längere Listen ist der Algorithmus praktisch nutzlos und unpraktikabel. Es ist viel effizienter, andere Sortieralgorithmen zu verwenden, die eine bessere Leistungsgarantie bieten.
+63
View File
@@ -0,0 +1,63 @@
/*
(a) hat eine Zeitkomplexität von O(n), wobei n die Anzahl der Elemente in vec ist.
Das liegt daran, dass jede Operation in der for-Schleife in konstanter Zeit ausgeführt wird
und die Schleife genau n-Mal durchlaufen wird.
*/
int vector_summe(vector<int>& vec){
int sum = 0;
for (unsigned i =0; i < vec.size(); i++){
sum +=vec[i];
}
return sum;
}
/*
(b) hat eine Zeitkomplexität von O(n^2), da es zwei verschachtelte Schleifen gibt,
die jeweils bis zur Größe des Vektors laufen.
Dies bedeutet, dass für jedes Element in vec alle Elemente innerhalb des Elementes einmal betrachtet werden,
was zu n*n Operationen führt.
*/
vector<pair<int, int>> pairs(vector<int>& vec){
vector<pair<int, int>> returnpairs;
pair<int, int> p;
for(unsigned i = 0; i<vec.size(); i++){
for(unsigned j=0; j<vec.size(); j++){
p = {vec[i], vec[j]};
returnpairs.push_back(p);
}
}
return returnpairs;
}
/*
(c) Die Eingabegröße n ist die Größe von vec1 und m ist die Größe von vec2.
Zuerst werden die separate Listen, vec1 und vec2, separat sortiert.
Die Zeitkomplexität für das Sortieren von vec1 ist O(n log n) und O(m log m) für das Sortieren von vec2,
da das die Zeitkomplexität von mergesort ist.
Die anschließenden verschachtelten Schleifen, die über die beiden Vektoren iterieren, haben eine Zeitkomplexität von O(n*m),
da jede Kombination aus Elementen von vec1 und vec2 einmal betrachtet wird.
Daher ist die gesamte Zeitkomplexität O(n log n + m log m + n*m).
Die Anzahl der Operationen entspricht im Wesentlichen der Zeitkomplexität.
*/
vector<pair<int, int>> sorted_pairs(vector<int> vec1, vector<int> vec2){
vector<pair<int, int>> returnpairs;
pair<int, int> p;
mergeSort(vec1);
mergeSort(vec2);
for(unsigned i = 0; i < vec1.size(); i++){
for(unsigned j = 0; j < vec2.size(); j++){
p = {vec1[i], vec2[j]};
returnpairs.push_back(p);
}
}
}
+77
View File
@@ -0,0 +1,77 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 12.05.2023
Katharina Klost
Hilfsfunktionen zum Sortieren, nur zusammen mit der sorting.cpp zu compilieren
*/
#include <iostream>
#include <vector>
#include "hilfsfunktionen.hpp"
namespace hf{
void gebe_aus(std::vector<int>& vec, unsigned start, unsigned end){
if(end>vec.size()){
std::cout << vec.size() << " " << end << std::endl;
return;
}
std::cout << vec[start];
for(unsigned k=start+1; k<end; k++){
std::cout << ", " << vec[k];
}
std::cout << std::endl;
}
void swap(std::vector<int>& elems, unsigned i, unsigned j){
if (i>=elems.size() || j >= elems.size()){
return;
}
int tmp = elems[i];
elems[i] = elems[j];
elems[j] = tmp;
}
unsigned minimumIndex(std::vector<int>& elems, unsigned start, unsigned end){
unsigned min = start;
for(unsigned i = start; i<end; i++){
if(elems[i] < elems[min]){
min = i;
}
}
return min;
}
std::vector<int> copy(std::vector<int>& elems, unsigned start, unsigned end){
std::vector<int> returnVec;
for(unsigned i= start; i<end; i++){
returnVec.push_back(elems[i]);
}
return returnVec;
}
bool generator_started = false;
int zufall(int left, int right) {
// Vor der ersten Zufallszahl muss der Zufallsgenerator gestartet werden.
if (!generator_started) {
srandom(time(NULL));
generator_started = true;
}
return left + random() % (right-left);
}
std::vector<int> sortiertes_array(unsigned size, bool aufsteigend) {
std::vector<int> array(size);
for (unsigned i = 0; i < size; i++)
array[i] = aufsteigend * i + (!aufsteigend) * (size - i - 1);
return array;
}
std::vector<int> zufalls_array(unsigned size) {
std::vector<int> array(size);
for (unsigned i = 0; i < size; i++)
array[i] = zufall(0, size);
return array;
}
}
+66
View File
@@ -0,0 +1,66 @@
/* Informatik B - Sommersemester 2023
Vorlesung am 12.05.2023
Katharina Klost
Headerdatei zu den Hilfsfunktionen zum Sortieren
*/
#include <vector>
// #include "telefonbuch.hpp"
namespace hf{
/*
Voraussetzung: 0 <= start <= end < vec.elems()
Ergebnis: Keins
Effekt: Gibt die Elemente des Vektors im Intervall [start,end) aus
*/
void gebe_aus(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: 0<=start<=end < vec.elems()
Ergebnis: Ein vector, mit alle Elemente im Intervall [start, end)
Effekt: Keiner
*/
std::vector<int> copy(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: 0 <= i,j <= elems.size()
Ergebnis: Keins
Effekt: Tauscht die Einträge and Position i und j
*/
void swap(std::vector<int>& elems, unsigned i, unsigned j);
/*
Voraussetzung: Es gilt 0 <= start <= end < elems.size()
Effekt: Keiner
Ergebni: Index des kleinesten Elements zwischen start und end
*/
unsigned minimumIndex(std::vector<int>& elems, unsigned start, unsigned end);
/*
Voraussetzung: left < right
Ergebnis: Eine Zufallszahl zwischen left(inklusive) und right (exklusive)
Effekt: Keiner
*/
int zufall(int left, int right);
/*
Voraussetzung: Keine
Effekt: Keiner
Ergebnis: Ein sortierter vector ist geliefert. Wenn aufsteigend=true ist der vector aufsteigend sortiert, sonst absteigends
*/
std::vector<int> sortiertes_array(unsigned size, bool aufsteigend);
/*
Voraussetzung: Keine
Effekt: Keiner
Ergebnis: Ein vector mit zufälligen Werten ist geliefert. Hierbei wird jede Zahl gleichverteilt
zufällig zwischen 0 und size gewählt. Doppelte Zahlen sind möglich.
*/
std::vector<int> zufalls_array(unsigned size);
}
+22
View File
@@ -0,0 +1,22 @@
//g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w5/min.cpp -o ./bin/min && ./bin/min
#include <iostream>
#include <vector>
int min(const std::vector<int>& vec);
int main() {
std::vector<int> testVec = {10, 20, 2, 0, 30};
std::cout << "The minimum value in the vector is: " << min(testVec) << std::endl;
}
int min(const std::vector<int>& vec) {
int minVal = vec[0];
for (unsigned i = 1; i < vec.size(); ++i) {
if (vec[i] < minVal) {
minVal = vec[i];
}
}
return minVal;
}
+39
View File
@@ -0,0 +1,39 @@
/*
clear && g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w5/turn_min.cpp -o ./bin/turn_min && ./bin/turn_min
Bei jedem Durchlauf halbieren wir die Größe der Eingabe (also n/2),
und wir müssen alle Elemente betrachten, um das Minimum zu finden (also O(n))
Daraus folgt die Rekursionsgleichung: T(n) = T(n/2) + O(n)
Da wir log(n) Runden benötigen und in jeder Runde O(n) Operationen durchführen
beträgt die Gesamtlaufzeit des Algorithmus O(n log n)
*/
#include <iostream>
#include <vector>
std::pair<int, int> TurnMin(std::vector<int>& vec);
int main() {
std::vector<int> vec = {7, 3, 2, 5, 10, -5, 4, 6,2,2,3,4,4,5,6,76,7,78};
auto ergebnis = TurnMin(vec);
std::cout << "Die Gewinnerzahl, nach " << ergebnis.first << " Runden, ist: " << ergebnis.second << std::endl;
}
std::pair<int, int> TurnMin(std::vector<int>& vec) {
int runde = 0;
while (vec.size() > 1) {
std::vector<int> gewinner;
for (unsigned i = 0; i < vec.size(); i += 2) {
if (vec[i] < vec[i + 1]) {
gewinner.push_back(vec[i]);
}
else {
gewinner.push_back(vec[i + 1]);
}
}
vec = gewinner;
runde++;
}
return {runde, vec[0]};
}
+185
View File
@@ -0,0 +1,185 @@
// clear && g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w5/hilfsfunktionen.cpp ./w5/ultrasort.cpp -o ./bin/ultrasort && ./bin/ultrasort
#include "hilfsfunktionen.hpp"
#include <iostream>
#include <vector>
#include <limits>
bool isSorted(const std::vector<int>& vec);
void insertionSort(std::vector<int>& elems);
void merge(std::vector<int>& first_part, std::vector<int>& second_part, std::vector<int>& elems);
void ultraSort(std::vector<int>& elems);
void ultraSortParam(std::vector<int>& elems, double k);
int main() {
srand(time(NULL));
double time;
std::vector<int> array;
// Runtime (a)
std::cout << "############ RUNTIME (ultraSort) ############ \n";
for (int size = 10000; size <= 100000; size *= 10) {
std::cout << " " << size << " Elemente:" << std::endl;
array = hf::zufalls_array(size);
time = clock();
ultraSort(array);
time = clock() - time;
std::cout << " Zufällig: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
array = hf::sortiertes_array(size, true);
time = clock();
ultraSort(array);
time = clock() - time;
std::cout << " Aufsteigend: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
array = hf::sortiertes_array(size, false);
time = clock();
ultraSort(array);
time = clock() - time;
std::cout << " Absteigend: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
if (isSorted(array)) {
std::cout << " Array is sorted.\n";
} else {
std::cout << " Array is not sorted.\n";
}
}
// Optimal k
std::cout << "############ OPTIMAL K ############ \n";
std::vector<double> k_values = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
double optimal_k = 0;
double shortest_time = std::numeric_limits<double>::max();
for (double k : k_values) {
array = hf::zufalls_array(10000);
time = clock();
ultraSortParam(array, k);
time = clock() - time;
if (time < shortest_time) {
shortest_time = time;
optimal_k = k;
}
}
std::cout << " Optimal k is " << optimal_k << std::endl;
// Runtime (b)
std::cout << "############ RUNTIME (ultraSortParam) ############ \n";
for (int size = 10000; size <= 100000; size *= 10) {
std::cout << " " << size << " Elemente:" << std::endl;
array = hf::zufalls_array(size);
time = clock();
ultraSortParam(array, optimal_k);
time = clock() - time;
std::cout << " Zufällig: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
array = hf::sortiertes_array(size, true);
time = clock();
ultraSortParam(array, optimal_k);
time = clock() - time;
std::cout << " Aufsteigend: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
array = hf::sortiertes_array(size, false);
time = clock();
ultraSortParam(array, optimal_k);
time = clock() - time;
std::cout << " Absteigend: " << (time/CLOCKS_PER_SEC) << " Sekunden" << std::endl;
if (isSorted(array)) {
std::cout << " Array is sorted.\n";
} else {
std::cout << " Array is not sorted.\n";
}
}
return 0;
}
bool isSorted(const std::vector<int>& vec) {
for (size_t i = 0; i < vec.size() - 1; ++i) {
if (vec[i] > vec[i + 1]) {
return false;
}
}
return true;
}
void insertionSort(std::vector<int>& elems){
int key=elems[0];
unsigned shiftindex;
for(unsigned i = 1; i<elems.size();i++){
key=elems[i];
for(shiftindex=i; shiftindex>0&&elems[shiftindex-1]>=key;shiftindex--){
elems[shiftindex] = elems[shiftindex-1];
}
elems[shiftindex] = key;
}
}
void merge(std::vector<int>& first_part, std::vector<int>& second_part, std::vector<int>& elems){
unsigned i = 0;
unsigned j = 0;
unsigned begin = 0;
unsigned end = elems.size();
while(begin + i + j < end){
if ( j >= second_part.size() || ((i<first_part.size()) && (first_part[i]<second_part[j]))){
elems[begin+i+j] = first_part[i];
i++;
}
else{
elems[begin+i+j] = second_part[j];
j++;
}
}
}
void ultraSort(std::vector<int>& elems){
if (elems.size() <= 1) {
return;
}
if (elems.size() < 10) {
insertionSort(elems);
return;
}
int groesse_erster_teil = elems.size() / 3;
std::vector<int> erster_teil(elems.begin(), elems.begin() + groesse_erster_teil);
std::vector<int> zweiter_teil(elems.begin() + groesse_erster_teil, elems.end());
insertionSort(erster_teil);
ultraSort(zweiter_teil);
merge(erster_teil, zweiter_teil, elems);
}
void ultraSortParam(std::vector<int>& elems, double k){
if (elems.size() <= 1) {
return;
}
if (elems.size() < 10) {
insertionSort(elems);
return;
}
int groesse_erster_teil = static_cast<int>(k * elems.size());
std::vector<int> erster_teil(elems.begin(), elems.begin() + groesse_erster_teil);
std::vector<int> zweiter_teil(elems.begin() + groesse_erster_teil, elems.end());
insertionSort(erster_teil);
ultraSortParam(zweiter_teil, k);
merge(erster_teil, zweiter_teil, elems);
}
+66
View File
@@ -0,0 +1,66 @@
#include <vector>
#include <iostream>
void bubbleSort(std::vector<int>& zahlen) {
unsigned n = zahlen.size();
for (unsigned i = 0; i < n - 1; ++i) {
for (unsigned j = 0; j < n - i - 1; ++j) {
if (zahlen[j] > zahlen[j + 1]) {
std::swap(zahlen[j], zahlen[j + 1]);
}
}
}
}
bool binareSuche(std::vector<int>& zahlen, int start, int ende, int ziel) {
while (start <= ende) {
int mitte = start + (ende - start) / 2;
if (zahlen[mitte] == ziel) {
return true;
} else if (zahlen[mitte] < ziel) {
start = mitte + 1;
} else {
ende = mitte - 1;
}
}
return false;
}
/*
Funktion: sucheTriplets(std::vector<int>& X)
Voraussetzungen:
- Die Funktion nimmt als Argument einen Vektor von Ganzzahlen (std::vector<int>).
- Der Vektor kann leer sein oder eine beliebige Anzahl von Ganzzahlen enthalten.
- Die Zahlen im Vektor können positiv, negativ oder null sein.
- Es wird angenommen, dass die Größe des Vektors in einem Bereich liegt, der für die verfügbare Speicherkapazität und Rechenleistung des Systems handhabbar ist.
Ergebnis:
- Die Funktion gibt kein Ergebnis zurück (void Rückgabetyp).
- Die Funktion druckt alle Tripel von Zahlen in der Eingabesequenz aus, für die die Summe der ersten beiden Zahlen gleich der dritten Zahl ist. Jedes Tripel wird in der Form "(a, b, c)" ausgegeben, wobei "a + b = c" gilt.
- Wenn keine solchen Tripel existieren, gibt die Funktion nichts aus.
Effekte:
- Die Funktion sortiert den Eingabevektor. Daher wird der Zustand des Eingabevektors geändert: die Elemente werden in aufsteigender Reihenfolge angeordnet.
- Die Funktion hat keinen weiteren Seiteneffekt auf den Zustand des Programms. Sie ändert keine globalen Variablen und hat keine Auswirkungen auf den Zustand des Systems außerhalb des Programms.
- Die Funktion kann eine beträchtliche Menge an Ausgabe produzieren, wenn viele gültige Tripel existieren. Dies könnte die Leistung des Systems beeinträchtigen, wenn die Ausgabe in eine Datei umgeleitet wird oder wenn das System über begrenzte Ressourcen für die Ausgabeverwaltung verfügt.
- Die Funktion verwendet zusätzlichen Speicher für das unordered_set, das zur Speicherung der Elemente des Eingabevektors verwendet wird. Die Menge des benötigten Speichers ist proportional zur Größe des Eingabevektors.
*/
void findeTripel(std::vector<int>& zahlen) {
bubbleSort(zahlen);
for (unsigned i = 0; i < zahlen.size(); ++i) {
for (unsigned j = i + 1; j < zahlen.size(); ++j) {
int summe = zahlen[i] + zahlen[j];
if (binareSuche(zahlen, 0, zahlen.size() - 1, summe)) {
std::cout << "(" << zahlen[i] << ", " << zahlen[j] << ", " << summe << ")\n";
}
}
}
}
int main() {
std::vector<int> zahlen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
findeTripel(zahlen);
return 0;
}
+107
View File
@@ -0,0 +1,107 @@
// g++ ./w6/heap.cpp -o ./bin/heap && ./bin/heap
#include <iostream>
#include <vector>
class MaxHeap {
private:
std::vector<int> heap;
// Hilfsfunktionen zum Vertauschen von Elementen und zur Berechnung von Eltern- und Kindknotenindizes
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int getParentIndex(int index) {
return (index - 1) / 2;
}
int getLeftChildIndex(int index) {
return (2 * index) + 1;
}
int getRightChildIndex(int index) {
return (2 * index) + 2;
}
// Hilfsfunktion zum Wiederherstellen der Heap-Eigenschaft nach oben
void heapifyUp(int index) {
if (index == 0)
return;
int parentIndex = getParentIndex(index);
if (heap[parentIndex] < heap[index]) {
swap(heap[parentIndex], heap[index]);
heapifyUp(parentIndex);
}
}
// Hilfsfunktion zum Wiederherstellen der Heap-Eigenschaft nach unten
void heapifyDown(int index) {
int largest = index;
int leftChildIndex = getLeftChildIndex(index);
int rightChildIndex = getRightChildIndex(index);
if (leftChildIndex < heap.size() && heap[leftChildIndex] > heap[largest])
largest = leftChildIndex;
if (rightChildIndex < heap.size() && heap[rightChildIndex] > heap[largest])
largest = rightChildIndex;
if (largest != index) {
swap(heap[index], heap[largest]);
heapifyDown(largest);
}
}
public:
// Einfügen eines Elements in den Heap
void insert(int value) {
heap.push_back(value);
heapifyUp(heap.size() - 1);
}
// Entfernen des Maximums aus dem Heap
int removeMax() {
if (heap.empty())
throw std::out_of_range("Heap is empty!");
int max = heap[0];
heap[0] = heap.back();
heap.pop_back();
heapifyDown(0);
return max;
}
// Zugriff auf das Maximum im Heap
int getMax() {
return heap[0];
}
// Prüfen, ob der Heap leer ist
bool isEmpty() {
return heap.empty();
}
};
int main() {
MaxHeap heap;
// Beispielverwendung des Heaps
heap.insert(10);
heap.insert(7);
heap.insert(15);
heap.insert(30);
heap.insert(20);
std::cout << "Max: " << heap.getMax() << std::endl;
heap.removeMax();
std::cout << "Max after removal: " << heap.getMax() << std::endl;
return 0;
}
+170
View File
@@ -0,0 +1,170 @@
# 1. Aussagen zu $\Omega$
Zeigen Sie, dass die Folgenden Aussagen gelten, geben Sie jeweils ein $n_0$ und $c$ an.
# (a) Wenn $a \cdot f(n) \in \Omega(b \cdot f(n))$ für beliebige Konstanten $a,b$.
Die Definition von $\Omega$ besagt, dass $f(n) \in \Omega(g(n))$, wenn es Konstanten $c>0$ und $n_0$ gibt, so dass $f(n) \geq c \cdot g(n)$ für alle $n \geq n_0$.
In diesem Fall ist $g(n)=b \cdot f(n)$, also muss $a \cdot f(n) \geq c \cdot b \cdot f(n)$ für alle $n \geq n_0$.
Wenn wir beide Seiten durch $f(n)$ teilen (vorausgesetzt, $f(n) \neq 0$ ), erhalten wir $a \geq c \cdot b$.
Da $a$ und $b$ Konstanten sind, können wir $c=a / b$ wählen, und die Aussage gilt für alle $n$, also ist $n_0=0$.
# (b) Für $0 \leq b \leq a$ gilt $n^a \in \Omega\left(n^b\right)$.
Die Aussage, dass $n^a \in \Omega\left(n^b\right)$ für $0 \leq b \leq a$, bedeutet, dass es eine Konstante $c>0$ und ein $n_0 \geq 1$ gibt, so dass für alle $n \geq n_0$ gilt, dass $n^a \geq c \cdot n^b$.
Da $b \leq a$, können wir $c=1$ wählen. Dann gilt für alle $n \geq 1$, dass $n^a \geq 1 \cdot n^b$, da das Erhöhen der Potenz einer Zahl (mit einer Basis größer als 1) zu einer größeren Zahl führt.
Also können wir $n_0=1$ und $c=1$ wählen, um die Aussage zu beweisen.
# (c) Wenn $f_1(n) \in \Omega\left(g_1(n)\right)$ und $f_2(n) \in \Omega\left(g_2(n)\right)$ dann ist auch $f_1(n) \cdot f_2(n) \in$ $\Omega\left(g_1(n) \cdot g_2(n)\right)$.
Da $f_1(n) \in \Omega\left(g_1(n)\right)$, gibt es Konstanten $c_1>0$ und $n_{01}$, so dass $f_1(n) \geq c_1$. $g_1(n)$ für alle $n \geq n_{01}$.
Ähnlich gibt es für $f_2(n) \in \Omega\left(g_2(n)\right)$ Konstanten $c_2>0$ und $n_{02}$ so dass $f_2(n) \geq c_2 \cdot g_2(n)$ für alle $n \geq n_{02}$.
Wenn wir diese beiden Ungleichungen multiplizieren, erhalten wir $f_1(n) \cdot f_2(n) \geq c_1 \cdot c_2 \cdot g_1(n) \cdot g_2(n)$ für alle $n \geq$ $\max \left(n_{01}, n_{02}\right)$.
Daher ist $f_1(n) \cdot f_2(n) \in \Omega\left(g_1(n) \cdot g_2(n)\right)$ mit $c=c_1 \cdot c_2$ und $n_0=$ $\max \left(n_{01}, n_{02}\right)$
# (d) Wenn $f_1(n) \in \Omega\left(g_1(n)\right)$ und $f_2(n) \in \Omega\left(g_2(n)\right)$ dann ist auch $f_1(n)+f_2(n) \in$ $\Omega\left(g_1(n)+g_2(n)\right)$
Da $f_1(n) \in \Omega\left(g_1(n)\right)$, gibt es Konstanten $c_1>0$ und $n_{01}$, so dass $f_1(n) \geq c_1$. $g_1(n)$ für alle $n \geq n_{01}$.
Ähnlich gibt es für $f_2(n) \in \Omega\left(g_2(n)\right)$ Konstanten $c_2>0$ und $n_{02}$, so dass $f_2(n) \geq c_2 \cdot g_2(n)$ für alle $n \geq n_{02}$.
Wenn wir diese beiden Ungleichungen addieren, erhalten wir $f_1(n)+f_2(n) \geq c_1 \cdot g_1(n)+c_2 \cdot g_2(n)$ für alle $n \geq$ $\max \left(n_{01}, n_{02}\right)$.
Da $c_1 \cdot g_1(n)+c_2 \cdot g_2(n) \geq \min \left(c_1, c_2\right) \cdot\left(g_1(n)+g_2(n)\right)$, können wir $c=\min \left(c_1, c_2\right)$ wählen, und $f_1(n)+f_2(n) \in \Omega\left(g_1(n)+g_2(n)\right)$ mit $n_0=$ $\max \left(n_{01}, n_{02}\right)$
# 2. Rekursion und O-Notation
# (a) lösen Sie die folgenden Rekursionsgleichungen und geben Sie Ihren Lösungsweg an. Gehen Sie von T (0) = T (1) = O(1) aus.
## (i) $T(n)=3 T(n / 3)+3 n$
Wir verwenden das Master-Theorem, das für Rekursionen der Form $T(n)=a T(n / b)+f(n)$ gilt, wobei $a \geq 1, b>1$, und $f(n)$ eine positive Funktion ist. In diesem Fall haben wir $a=3$, $b=3$, und $f(n)=3 n$.
Nach dem Master-Theorem haben wir drei Fälle zu prüfen:
1. Wenn $f(n)=O\left(n^c\right)$, wobei $c<\log _b a$, dann ist $T(n)=$ $\Theta\left(n^{\log _b a}\right)$.
2. Wenn $f(n)=\Theta\left(n^c\right)$, wobei $c=\log _b a$, dann ist $T(n)=$ $\Theta\left(n^c \log n\right)$.
3. Wenn $f(n)=\Omega\left(n^c\right)$, wobei $c>\log _b a$, und wenn $a f(n / b) \leq k f(n)$ für ein $k<1$ und alle hinreichend großen $n$, dann ist $T(n)=\Theta(f(n))$.
In unserem Fall ist $f(n)=3 n=\Theta(n)$ und $\log _b a=\log _3 3=$ 1. Daher sind wir im zweiten Fall des Master-Theorems, und die Lösung ist $T(n)=\Theta(n \log n)$.
## (ii) $T(n)=T(n-1)+3$
Diese Rekursionsgleichung ist eine lineare Rekursion. Wir können sie lösen, indem wir sie mehrmals ausklappen:
$\begin{aligned} & T(n)=T(n-1)+3 \\ & =(T(n-2)+3)+3 \\ & =((T(n-3)+3)+3)+3 \\ & =\ldots \\ & =3 n+T(0)\end{aligned}$
$\operatorname{Da} T(0)=O(1)$, ist die Lösung $T(n)=\Theta(n)$.
## (iii) $T(n)=2 T\left(\frac{n}{3}\right)+3 n^2$
Wir können wieder das Master-Theorem verwenden. In diesem Fall haben wir $a=2, b=3$, und $f(n)=3 n^2$.
In unserem Fall ist $f(n)=3 n^2=\Omega\left(n^c\right)$ mit $c>\log _b a=$ $\log _3 2$, und die Bedingung $a f(n / b) \leq k f(n)$ für ein $k<1$ und alle hinreichend großen $n$ ist erfullt. Daher sind wir im dritten Fall des Master-Theorems, und die Lösung ist $T(n)=\Theta\left(n^2\right)$.
# (b) Sortieren Sie die Laufzeiten aufsteigend basierend auf der O-Notation.
1. $T(n)=\Theta(n)$
2. $T(n)=\Theta(n \log n)$
3. $T(n)=\Theta\left(n^2\right)$
# 3. Dreisummenproblem
Betrachten Sie das folgende Algorithmische Problem:
Gegeben: Eine Folge $X$ von Zahlen aus $\mathbb{Z}$.
Gesucht: Alle Tripel $a, b, c \in X$ von Zahlen mit mit $a+b=c$.
# (a) Beschreiben verbal oder mit Pseudocode einen naiven Algorithmus für dieses Problem. Was ist die Laufzeit Ihres Algorithmus?
```
für jedes Element a in X:
für jedes Element b in X:
für jedes Element c in X:
wenn a + b == c:
gib (a, b, c) aus
```
Die Laufzeit dieses Algorithmus ist $O\left(n^3\right)$, da wir drei verschachtelte Schleifen haben, die jeweils über die gesamte Sequenz iterieren.
# (b) Beschreiben verbal oder im Pseudocode Sie einen Algorithmus mit einer Laufzeit von $\Theta\left(n^2\right)$ für das Problem. Beweisen Sie, dass Ihr Algorithmus wirklich diese Laufzeit hat.
```
sortiere X
für jedes Element a in X:
für jedes Element b in X:
wenn a + b in X:
gib (a, b, a+b) aus
```
Die Laufzeit dieses Algorithmus ist $O\left(n^2\right)$ für die beiden verschachtelten Schleifen, die über die Sequenz iterieren, plus $O(n \log n)$ für das Sortieren der Sequenz. Da $n^2$ größer ist als $n \log n$ für große $n$, dominiert der $n^2$-Teil die Laufzeit, und wir können sagen, dass die Laufzeit des Algorithmus $O\left(n^2\right)$ ist.
# (c) Implementieren Sie Ihren effizienten Algorithmus in `C++`.
```cpp
#include <vector>
#include <iostream>
void bubbleSort(std::vector<int>& zahlen) {
unsigned n = zahlen.size();
for (unsigned i = 0; i < n - 1; ++i) {
for (unsigned j = 0; j < n - i - 1; ++j) {
if (zahlen[j] > zahlen[j + 1]) {
std::swap(zahlen[j], zahlen[j + 1]);
}
}
}
}
bool binareSuche(std::vector<int>& zahlen, int start, int ende, int ziel) {
while (start <= ende) {
int mitte = start + (ende - start) / 2;
if (zahlen[mitte] == ziel) {
return true;
} else if (zahlen[mitte] < ziel) {
start = mitte + 1;
} else {
ende = mitte - 1;
}
}
return false;
}
/*
Funktion: sucheTriplets(std::vector<int>& X)
Voraussetzungen:
- Die Funktion nimmt als Argument einen Vektor von Ganzzahlen (std::vector<int>).
- Der Vektor kann leer sein oder eine beliebige Anzahl von Ganzzahlen enthalten.
- Die Zahlen im Vektor können positiv, negativ oder null sein.
- Es wird angenommen, dass die Größe des Vektors in einem Bereich liegt, der für die verfügbare Speicherkapazität und Rechenleistung des Systems handhabbar ist.
Ergebnis:
- Die Funktion gibt kein Ergebnis zurück (void Rückgabetyp).
- Die Funktion druckt alle Tripel von Zahlen in der Eingabesequenz aus, für die die Summe der ersten beiden Zahlen gleich der dritten Zahl ist. Jedes Tripel wird in der Form "(a, b, c)" ausgegeben, wobei "a + b = c" gilt.
- Wenn keine solchen Tripel existieren, gibt die Funktion nichts aus.
Effekte:
- Die Funktion sortiert den Eingabevektor. Daher wird der Zustand des Eingabevektors geändert: die Elemente werden in aufsteigender Reihenfolge angeordnet.
- Die Funktion hat keinen weiteren Seiteneffekt auf den Zustand des Programms. Sie ändert keine globalen Variablen und hat keine Auswirkungen auf den Zustand des Systems außerhalb des Programms.
- Die Funktion kann eine beträchtliche Menge an Ausgabe produzieren, wenn viele gültige Tripel existieren. Dies könnte die Leistung des Systems beeinträchtigen, wenn die Ausgabe in eine Datei umgeleitet wird oder wenn das System über begrenzte Ressourcen für die Ausgabeverwaltung verfügt.
- Die Funktion verwendet zusätzlichen Speicher für das unordered_set, das zur Speicherung der Elemente des Eingabevektors verwendet wird. Die Menge des benötigten Speichers ist proportional zur Größe des Eingabevektors.
*/
void findeTripel(std::vector<int>& zahlen) {
bubbleSort(zahlen);
for (unsigned i = 0; i < zahlen.size(); ++i) {
for (unsigned j = i + 1; j < zahlen.size(); ++j) {
int summe = zahlen[i] + zahlen[j];
if (binareSuche(zahlen, 0, zahlen.size() - 1, summe)) {
std::cout << "(" << zahlen[i] << ", " << zahlen[j] << ", " << summe << ")\n";
}
}
}
}
int main() {
std::vector<int> zahlen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
findeTripel(zahlen);
return 0;
}
```
@@ -0,0 +1,27 @@
// g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w7/call_by_reference_pointer.cpp -o ./bin/call_by_reference_pointer && ./bin/call_by_reference_pointer
#include <iostream>
/*
Voraussetzungen:
Ein gültiger Zeiger auf ein Integer-Objekt muss als Argument an die Funktion übergeben werden.
Der Zeiger darf nicht null und darf nicht auf Speicher zeigen, der nicht verändert werden darf (z.B. konstante Daten oder bereits freigegebener Speicher).
Ergebnis:
Die Funktion hat keinen Rückgabewert.
Effekt:
Die Funktion verändert den Wert der Integer-Variablen, auf die der übergebene Zeiger zeigt, zu 10.
*/
void aendern(int *ptr)
{
*ptr = 10;
}
int main()
{
int x = 5;
std::cout << "Davor: " << x << std::endl;
aendern(&x);
std::cout << "Danach: " << x << std::endl;
}
+595
View File
@@ -0,0 +1,595 @@
\documentclass{article}
\usepackage{tikz-qtree}
\usepackage{xcolor}
\usepackage{listings}
\lstset{
language=C++,
basicstyle=\ttfamily,
keywordstyle=\color{blue},
stringstyle=\color{red},
commentstyle=\color{green!70!black},
showstringspaces=false,
breaklines=true,
frame=tb,
numbers=left,
numberstyle=\tiny,
numbersep=5pt,
tabsize=2
}
\title{7. Uebungszettel}
\author{Steinbock, Gottlebe}
\begin{document}
\maketitle
\section*{1. Heaps (a)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\centering
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree [.6 ]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree [.6 [.1 ]]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree [.6 [.1 ] [.22 ]]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.22
[.1
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.22
[.1
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.22
[.4
[.1
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.22
[.4
[.1
]
[.10
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.22
[.10
[.1
]
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.4
[.10
[.1
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.10
[.4
[.1
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.10
[.4
[.1
]
[.32
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.10
[.32
[.1
]
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.32
[.10
[.1
]
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.32
[.10
[.1
]
[.4
]
]
[.6
[.2
]
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.2
[.10
[.1
]
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.10
[.2
[.1
]
[.4
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.10
[.4
[.1
]
[.2
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}\hfill
\begin{minipage}{0.3\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.2
[.4
[.1
]
]
[.6
]
]
\end{tikzpicture}
\end{minipage}
\vspace{1cm}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{minipage}{0.45\textwidth}
\centering
\begin{tikzpicture}
\tikzset{every tree node/.style={minimum width=1.5em,draw,circle},
blank/.style={draw=none},
edge from parent/.style=
{draw, edge from parent path={(\tikzparentnode) -- (\tikzchildnode)}},
level distance=1.5cm}
\Tree
[.6
[.4
[.1
]
]
[.2
]
]
\end{tikzpicture}
\end{minipage}\hfill
\section*{1. Heaps (b)}
Die $\Omega$-Notation gibt eine untere Schranke für die Laufzeit eines Algorithmus an. Wenn wir sagen, dass ein Algorithmus eine Laufzeit von $\Omega (n \log n)$ hat, bedeutet das, dass der Algorithmus nicht schneller als $n \log n$ ausgeführt werden kann, für große Werte von $n$.
\vspace*{0.5cm}
Vergleichsbasiertes Sortieren hat eine untere Schranke von $\Omega (n \log n)$. Das bedeutet, dass jeder vergleichsbasierte Sortieralgorithmus mindestens $n \log n$ Vergleiche benötigt, um eine Liste von $n$ Elementen zu sortieren, im schlimmsten Fall.
\vspace*{0.5cm}
Nun betrachten wir eine Sequenz von n insert-Operationen und $n$ extractMax-Operationen auf einer vergleichsbasierten Datenstruktur. Jede insert-Operation fügt ein Element in die Datenstruktur ein und jede extractMax-Operation entfernt das größte Element aus der Datenstruktur. Nachdem alle insert-Operationen ausgeführt wurden, sind alle Elemente in der Datenstruktur und die extractMax-Operationen entfernen die Elemente in absteigender Reihenfolge. Dies entspricht im Wesentlichen einem Sortierprozess.
\vspace*{0.5cm}
Da die Datenstruktur vergleichsbasiert ist, muss sie für jede insert- und extractMax-Operation Vergleiche durchführen, um das Element an der richtigen Stelle einzufügen bzw. das größte Element zu finden. Da wir insgesamt $2n$ Operationen durchführen ($n$ insert-Operationen und $n$ extractMax-Operationen) und jede Operation mindestens $\log n$ Vergleiche erfordert (da wir ein Element in einer sortierten Liste von $n$ Elementen einfügen bzw. das größte Element finden), haben wir insgesamt mindestens $2n \log n$ Vergleiche.
\vspace*{0.5cm}
Da $2n \log n = \Omega(n \log n)$ (da wir nur an der Größenordnung interessiert sind und konstante Faktoren ignorieren), haben wir gezeigt, dass eine Sequenz von $n$ insert-Operationen und $n$ extractMax-Operationen auf einer vergleichsbasierten Datenstruktur eine Gesamtlaufzeit von $\Omega(n \log n)$ hat.
\section*{2. Pointer und Referenzen (a)}
Ein Pointer ist eine Variable, die die Adresse (Speicherstelle) einer Variable speichert.
Eine Referenz ist ein zusätzlicher Name für eine Variable.
\section*{2. Pointer und Referenzen (b)}
Call-by-Reference ist eine Methode, die in einigen Programmiersprachen zur Übergabe von Argumenten an Funktionen verwendet wird. Bei dieser Methode wird eine Referenz auf die Originalvariable, anstatt deren Wert, an die Funktion übergeben. Dies ermöglicht es der Funktion, den Wert der Originalvariable zu ändern.
\vspace*{0.5cm}
\begin{lstlisting}[language=C++]
#include <iostream>
void aendern(int *ptr)
{
*ptr = 10;
}
int main()
{
int x = 5;
std::cout << "Davor: " << x << std::endl;
aendern(&x);
std::cout << "Danach: " << x << std::endl;
}
\end{lstlisting}
\section*{2. Pointer und Referenzen (c)}
\begin{lstlisting}[language=C++]
void increase(int* aptr){
(*aptr)++;
}
\end{lstlisting}
\vspace*{0.5cm}
Spezifikation:
Die Funktion erhält einen Zeiger auf einen Integer-Wert (int* aptr) als Eingabe.
Sie erhöht den Wert, auf den der Zeiger zeigt, um eins.
Die Funktion hat keinen Rückgabewert (void).
\vspace*{0.5cm}
In der Regel spricht man von Call-by-Value, wenn der übergebene Wert in der Funktion nicht verändert wird und von Call-by-Reference, wenn der Originalwert modifiziert werden kann. In diesem Fall übergibt die Funktion den Speicherort (also den Zeiger) der zu ändernden Variable, nicht den Wert selbst. Das bedeutet, dass die Änderungen, die in der Funktion vorgenommen werden, sich auf die ursprüngliche Variable auswirken, auf die der Zeiger zeigt. Daher ist dies ein Fall von Call-by-Reference.
\section*{2. Pointer und Referenzen (d)}
\begin{lstlisting}[language=C++]
#include <iostream>
int summeZahlen()
{
int k;
std::cout << "Geben Sie k an: ";
std::cin >> k;
int *arr = new int[k];
for (int i = 0; i < k; ++i)
{
std::cout << "Geben Sie eine Zahl ein: ";
std::cin >> arr[i];
}
int summe = 0;
for (int i = 0; i < k; ++i)
{
summe += arr[i];
}
delete[] arr;
return summe;
}
int main()
{
int summe = summeZahlen();
std::cout << "Die Summe ist: " << summe << std::endl;
}
\end{lstlisting}
\newpage
\section*{3. Noch mehr Pointer und Referenzen (a)}
\begin{table}[h]
\centering
\begin{tabular}{|c|c|c|}
\hline
\textbf{Adresse} & \textbf{Variable} & \textbf{Wert} \\
\hline
0x16cf5af7c & a & 42 \\
0x16cf5af78 & b & 6 \\
0x16cf5af70 & c & \&a 0x16cf5af7c \\
0x16cf5af68 & d & \&b 0x16cf5af78 \\
0x16cf5af7c & e & 42 (gleiche Adresse wie a) \\
\hline
\end{tabular}
\caption{Speicheraufbau nach der ersten Änderung}
\end{table}
\begin{table}[h]
\centering
\begin{tabular}{|c|c|c|}
\hline
\textbf{Adresse} & \textbf{Variable} & \textbf{Wert} \\
\hline
0x16cf5af7c & a & 252 \\
0x16cf5af78 & b & 756 \\
0x16cf5af70 & c & \&a 0x16cf5af7c \\
0x16cf5af68 & d & 3 \\
0x16cf5af7c & e & 252 (gleiche Adresse wie a) \\
\hline
\end{tabular}
\caption{Speicheraufbau nach der zweiten Änderung}
\end{table}
\begin{table}[h]
\centering
\begin{tabular}{|c|c|c|}
\hline
\textbf{Adresse} & \textbf{Variable} & \textbf{Wert} \\
\hline
0x16cf5af7c & a & 14 \\
0x16cf5af78 & b & 7 \\
0x16cf5af70 & c & \&a 0x16cf5af7c \\
0x16cf5af68 & d & 3 \\
0x16cf5af7c & e & 14 (gleiche Adresse wie a) \\
\hline
\end{tabular}
\caption{Speicheraufbau nach der dritten Änderung}
\end{table}
\section*{3. Noch mehr Pointer und Referenzen (b)}
ref(a) ist eine Funktion, die eine Referenz auf a nimmt (also die Speicheradresse von a). Daher ist \&alpha gleich \&a und alpha gleich a. Nach dem Aufruf der Funktion wird der Wert von a um eins erhöht (wegen alpha++), deshalb ist a jetzt 15.
\vspace*{0.5cm}
val(a) ist eine Funktion, die den Wert von a nimmt, aber eine neue Kopie des Wertes erstellt (weil es Call-by-Value ist). Daher ist alpha gleich a, aber \&alpha ist nicht gleich \&a. Nach dem Aufruf der Funktion bleibt a 15, weil die Erhöhung von alpha nur auf die Kopie angewendet wird, nicht auf das Original a.
\vspace*{0.5cm}
func1(c) ist eine Funktion, die den Wert von c nimmt, der die Adresse von a ist. Daher ist alpha gleich c und \&alpha ist eine neue Speicheradresse (die Adresse des Zeigers selbst, nicht die Adresse, auf die er zeigt). Nach dem Aufruf der Funktion wird c verändert, weil alpha++ den Zeiger verschiebt, aber a bleibt unverändert.
\vspace*{0.5cm}
func2(c) ist eine Funktion, die den Wert von c nimmt, der die Adresse von a ist. Daher ist alpha gleich c und \&alpha ist eine neue Speicheradresse (die Adresse des Zeigers selbst, nicht die Adresse, auf die er zeigt). Nach dem Aufruf der Funktion wird der Wert von a erhöht (wegen (*alpha)++), deshalb ist a jetzt 16. Aber c bleibt unverändert, weil es immer noch auf die Adresse von a zeigt.
\section*{3. Noch mehr Pointer und Referenzen (c)}
Das Problem ist, dass a und e nicht als Zeiger definiert sind, sondern als int. Sie sind normale Integer-Variablen und haben keine Speicheradresse, auf die sie verweisen. Das Dereferenzieren von a und e ist daher nicht zulässig und führt zu einem Fehler.
\end{document}
+46
View File
@@ -0,0 +1,46 @@
// g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w7/primitive_arrays.cpp -o ./bin/primitive_arrays && ./bin/primitive_arrays
#include <iostream>
/*
Voraussetzungen:
Die Funktion sollte nur aufgerufen werden, wenn eine Eingabe vom Benutzer erwartet wird.
Es wird vorausgesetzt, dass der Benutzer ausschließlich positive Ganzzahlen eingibt.
Ergebnis:
Die Funktion gibt die Summe aller vom Benutzer eingegebenen Zahlen als Integer zurück.
Effekt:
Die Funktion reserviert und gibt dynamischen Speicher frei.
*/
int summeZahlen()
{
int k;
std::cout << "Geben Sie k an: ";
std::cin >> k;
int *arr = new int[k];
for (int i = 0; i < k; ++i)
{
std::cout << "Geben Sie eine Zahl ein: ";
std::cin >> arr[i];
}
int summe = 0;
for (int i = 0; i < k; ++i)
{
summe += arr[i];
}
delete[] arr;
return summe;
}
int main()
{
int summe = summeZahlen();
std::cout << "Die Summe ist: " << summe << std::endl;
}
+135
View File
@@ -0,0 +1,135 @@
/* Informatik B - Sommersemester 2022
Übung 06
Katharina Klost
Aufgabe zu Pointern
g++ -std=c++14 -Wall -Wextra -Werror -pedantic ./w7/ueb7pointer.cpp -o ./bin/ueb7pointer && ./bin/ueb7pointer
*/
#include <iostream>
void ref(int &alpha)
{ // Call-by-Reference
std::cout << "&alpha: " << (&alpha) << std::endl;
std::cout << "alpha: " << (alpha) << std::endl;
alpha++;
}
void val(int alpha)
{ // Call-by-Value
std::cout << "&alpha: " << (&alpha) << std::endl;
std::cout << "alpha: " << (alpha) << std::endl;
alpha++;
}
void func1(int *alpha)
{
std::cout << "&alpha: " << (&alpha) << std::endl;
std::cout << "alpha: " << (alpha) << std::endl;
alpha++;
}
void func2(int *alpha)
{
std::cout << "&alpha: " << (&alpha) << std::endl;
std::cout << "alpha: " << (alpha) << std::endl;
(*alpha)++;
}
int main()
{
// Zeichnen Sie Speicherbilder, die veranschaulichen Sie was hier passiert.
// Erklären Sie die entsprechenden Ausgaben.
int a = 42;
int b = 6;
int *c = &a;
int *d = &b;
int &e = a;
std::cout << "a: " << a << std::endl;
std::cout << "b: " << b << std::endl;
std::cout << "c: " << c << std::endl;
std::cout << "d: " << d << std::endl;
std::cout << "e: " << e << std::endl;
std::cout << std::endl;
std::cout << "&a: " << (&a) << std::endl;
std::cout << "&b: " << (&b) << std::endl;
std::cout << "&c: " << (&c) << std::endl;
std::cout << "&d: " << (&d) << std::endl;
std::cout << "&e: " << (&e) << std::endl;
std::cout << std::endl;
// std::cout << (*a) << std::endl; Warum geht das nicht?
// std::cout << (*e) << std::endl; Warum geht das nicht?
std::cout << "*c: " << (*c) << std::endl;
std::cout << "*d: " << (*d) << std::endl;
std::cout << std::endl;
std::cout << "=== Skizzieren Sie den Aufbau des Speichers zu diesem Zeitpunkt ===" << std::endl;
std::cout << std::endl;
a = *c * *d;
std::cout << "a: " << a << std::endl;
*d -= 3;
std::cout << "*d: " << (*d) << std::endl;
b = a * b;
std::cout << "b: " << b << std::endl;
c = d;
std::cout << "*c: " << (*c) << std::endl;
std::cout << std::endl;
std::cout << "=== Skizzieren Sie den Aufbau des Speichers zu diesem Zeitpunkt ===" << std::endl;
std::cout << std::endl;
b = 7;
std::cout << "b: " << b << std::endl;
a = *c + *d;
std::cout << "a: " << a << std::endl;
std::cout << "&e: " << (&e) << std::endl;
std::cout << std::endl;
std::cout << std::endl;
c = &a;
std::cout << std::endl;
std::cout << "=== Skizzieren Sie den Aufbau des Speichers zu diesem Zeitpunkt ===" << std::endl;
std::cout << std::endl;
std::cout << std::endl;
std::cout << "== Erklären Sie die folgenden Ausgaben ==" << std::endl;
std::cout << std::endl;
std::cout << "a: " << a << std::endl;
std::cout << "&a: " << (&a) << std::endl;
std::cout << "Rufe ref(a) auf" << std::endl;
ref(a);
std::cout << std::endl;
std::cout << "a: " << a << std::endl;
std::cout << "&a: " << (&a) << std::endl;
std::cout << "Rufe val(a) auf" << std::endl;
val(a);
std::cout << std::endl;
std::cout << "a: " << a << std::endl;
std::cout << "c: " << c << std::endl;
std::cout << "&c: " << (&c) << std::endl;
std::cout << "Rufe func1(c) auf" << std::endl;
func1(c);
std::cout << std::endl;
std::cout << "c: " << c << std::endl;
std::cout << "&c: " << (&c) << std::endl;
std::cout << "Rufe func2(c) auf" << std::endl;
func2(c);
std::cout << "c: " << c << std::endl;
std::cout << "&c: " << (&c) << std::endl;
std::cout << "a: " << a << std::endl;
}
+162
View File
@@ -0,0 +1,162 @@
#include <string>
#include <iostream>
/*
EScooter
-------------------------
- Marke: std::string
- PS: int
- Akku: int
-------------------------
+ EScooter(m: std::string, p: int, a: int)
+ ~EScooter()
+ getModell(): std::string
+ getPS(): int
+ getAkku(): int
+ setPS(p: int): void
+ operator<(other: EScooter): bool
*/
class EScooter
{
private:
// Attribute
std::string Marke;
int PS;
int Akku;
public:
/*
Voraussetzungen: Die Funktion kann mit einem String und zwei Integers aufgerufen werden.
Ergebnis: Es wird kein Ergebnis zurückgegeben.
Effekt: Ein neues EScooter-Objekt wird mit den angegebenen Marken-, PS- und Akkuwerten erstellt.
*/
EScooter(std::string m, int p, int a) : Marke(m), PS(p), Akku(a) {}
/*
Voraussetzungen: Keine. Der Destruktor wird aufgerufen, wenn ein EScooter-Objekt gelöscht wird.
Ergebnis: Es wird kein Ergebnis zurückgegeben.
Effekt: Das EScooter-Objekt wird aus dem Speicher entfernt.
*/
~EScooter() {}
/*
Voraussetzungen: Diese Methoden können aufgerufen werden, um die entsprechenden Attribute eines EScooter-Objekts zu erhalten.
Ergebnis: Jede Methode gibt den entsprechenden Attributwert zurück.
Effekt: Kein weiterer Effekt.
*/
std::string getModell()
{
return Marke;
}
int getPS()
{
return PS;
}
int getAkku()
{
return Akku;
}
/*
Voraussetzungen: Die Methode kann aufgerufen werden, um den PS-Wert eines EScooter-Objekts zu ändern.
Ergebnis: Es wird kein Ergebnis zurückgegeben.
Effekt: Der PS-Wert des EScooter-Objekts wird auf den übergebenen Wert gesetzt.
*/
void setPS(int p)
{
PS = p;
}
/*
Voraussetzungen: Die Methode kann aufgerufen werden, um zwei EScooter-Objekte auf der Basis ihrer PS-Werte zu vergleichen.
Ergebnis: Die Methode gibt ein boolsches Ergebnis zurück, das angibt, ob das aktuelle EScooter-Objekt weniger PS hat als das andere.
Effekt: Kein weiterer Effekt.
*/
bool operator<(const EScooter &other) const
{
return PS < other.PS;
}
};
/*
Kochfeld
-------------------------
- Marke: std::string
- AnzahlBrenner: int
- istEingeschaltet: bool
-------------------------
+ Kochfeld(m: std::string, b: int)
+ ~Kochfeld()
+ getMarke(): std::strinsg
+ getAnzahlBrenner(): int
+ einschalten(): void
+ ausschalten(): void
+ istAn(): bool
*/
class Kochfeld
{
private:
std::string Marke;
int AnzahlBrenner;
bool istEingeschaltet;
public:
// Konstruktor
Kochfeld(std::string m, int b) : Marke(m), AnzahlBrenner(b), istEingeschaltet(false) {}
// Destruktor
~Kochfeld() {}
// Methoden
std::string getMarke() const
{
return Marke;
}
int getAnzahlBrenner() const
{
return AnzahlBrenner;
}
void einschalten()
{
istEingeschaltet = true;
}
void ausschalten()
{
istEingeschaltet = false;
}
bool istAn() const
{
return istEingeschaltet;
}
};
int main()
{
// Test EScooter
EScooter meinScooter("voi", 250, 100);
std::cout << "Marke: " << meinScooter.getModell() << std::endl;
std::cout << "PS: " << meinScooter.getPS() << std::endl;
std::cout << "Akku: " << meinScooter.getAkku() << std::endl;
EScooter andererScooter("lime", 300, 120);
std::cout << "Ist mein Scooter schwächer als der andere? " << (meinScooter < andererScooter ? "Ja" : "Nein") << std::endl;
meinScooter.setPS(350);
std::cout << "Nach dem Tuning: " << std::endl;
std::cout << "PS: " << meinScooter.getPS() << std::endl;
std::cout << "Ist mein Scooter jetzt schwächer als der andere? " << (meinScooter < andererScooter ? "Ja" : "Nein") << std::endl;
// Test Kochfeld
Kochfeld meinKochfeld("Bosch", 4);
std::cout << "Marke: " << meinKochfeld.getMarke() << std::endl;
std::cout << "Anzahl der Brenner: " << meinKochfeld.getAnzahlBrenner() << std::endl;
std::cout << "Ist das Kochfeld eingeschaltet? " << (meinKochfeld.istAn() ? "Ja" : "Nein") << std::endl;
meinKochfeld.einschalten();
std::cout << "Ist das Kochfeld eingeschaltet? " << (meinKochfeld.istAn() ? "Ja" : "Nein") << std::endl;
meinKochfeld.ausschalten();
std::cout << "Ist das Kochfeld eingeschaltet? " << (meinKochfeld.istAn() ? "Ja" : "Nein") << std::endl;
}
+64
View File
@@ -0,0 +1,64 @@
#include <iostream>
#include <vector>
class Konto
{
private:
unsigned pin;
int kontostand;
public:
Konto(unsigned pin, int kontostand) : pin(pin), kontostand(kontostand) {}
void abheben(unsigned pin, unsigned betrag)
{
if (this->pin == pin)
{
kontostand = kontostand - betrag;
}
else
{
std::cout << "Falsche Pin.\n";
}
}
void einzahlen(unsigned pin, unsigned betrag)
{
if (this->pin == pin)
{
std::cout << "Alter Kontostand: " << kontostand << std::endl;
kontostand = kontostand + betrag;
std::cout << "Neuer Kontostand: " << kontostand << std::endl;
}
else
{
std::cout << "Falsche Pin.\n";
}
}
void mehr_einzahlen(unsigned pin, std::vector<unsigned> betraege)
{
if (this->pin == pin)
{
for (unsigned i; i < betraege.size(); i++)
kontostand = kontostand + betraege[i];
}
else
{
std::cout << "Falsche Pin.\n";
}
}
~Konto()
{
std::cout << "Konto wurde gelöscht.\n";
}
};
int main()
{
int betrag;
std::cin >> betrag;
Konto meinKonto(1234, 6000);
meinKonto.einzahlen(1234, betrag);
}
+102
View File
@@ -0,0 +1,102 @@
/*
(a) Attribute und Hilfsmethoden sollen als private deklariert sein.
Dies gewährleistet die Datenkapselung und Datenintegrität, indem sichergestellt wird, dass interne Zustände und Hilfsmethoden nicht direkt von außerhalb der Klasse modifiziert werden können.
*/
class BeispielKlassePriPub
{
private:
int attribut; // Privates Attribut
void hilfsMethode()
{ // Private Hilfsmethode
attribut *= 2;
}
public:
BeispielKlassePriPub() : attribut(5) {}
void offentlicheMethode()
{
hilfsMethode();
}
};
/*
(b) Methoden, die public sind, sowie die Konstruktoren als auch der Destruktor sollen spezifiziert sein. (Hier müssen Sie keine Code-Beispiele angeben.)
Dies bietet eine schriftliche Schnittstelle für andere um die Klasse richtig benutzen zu können.
*/
/*
(c) Das Signalwort const soll immer dort benutzt werden, wo es sinnvoll ist.
Durch Verwendung von const können wir sicherstellen, dass eine Methode den internen Zustand des Objekts nicht ändert. Dies kann besonders hilfreich sein, wenn wir das Objekt in einer Situation verwenden möchten, in der es unverändert bleiben muss.
*/
class BeispielKlasseConst
{
private:
int attribut;
public:
BeispielKlasseConst(int wert) : attribut(wert) {}
// Eine const Methode, die das Attribut nicht ändern kann
int getAttribut() const
{
return attribut;
}
};
/*
(d) Jede Klasse, die mit new eigenen Speicher alloziert, soll einen Destruktor, einen Copy-Konstruktor und einen Copy-Assignment-Operator implementieren.
Dies sind die großen vier in C++. Wenn eine dieser Operationen benötigt wird, werden wahrscheinlich alle benötigt werden. Sie helfen dabei, Speicherlecks zu verhindern und sicherzustellen, dass das Objekt korrekt kopiert wird.
*/
class BeispielKlasse3
{
private:
int *attribut;
public:
// Konstruktor
BeispielKlasse3(int wert) : attribut(new int(wert)) {}
// Destruktor
~BeispielKlasse3()
{
delete attribut;
}
// Kopierkonstruktor
BeispielKlasse3(const BeispielKlasse3 &andere) : attribut(new int(*(andere.attribut))) {}
// Kopierzuweisungsoperator
BeispielKlasse3 &operator=(const BeispielKlasse3 &andere)
{
if (this != &andere)
{
delete attribut;
attribut = new int(*(andere.attribut));
}
return *this;
}
};
/*
(e) Benutze Call-by-Reference in Verbindung mit const statt Call-by-Value, wenn das übergebene Objekt nicht verändert werden soll.
Dies ist nützlich, um teure Kopien von großen Objekten zu vermeiden und gleichzeitig sicherzustellen, dass das ursprüngliche Objekt unverändert bleibt.
*/
class AndereKlasse
{
// Definition für AndereKlasse
};
class BeispielKlasse4
{
public:
void irgendeineMethode(const AndereKlasse &objekt) const
{
// kann objekt hier verwenden, aber es nicht ändern
}
};
+158
View File
@@ -0,0 +1,158 @@
#include <vector>
#include <iostream>
#include <string>
template <typename SchluesselTyp, typename WertTyp>
class schluesselWertPaar
{
private:
SchluesselTyp schluessel;
WertTyp wert;
public:
schluesselWertPaar() {}
// Konstruktor
schluesselWertPaar(SchluesselTyp schluessel, WertTyp wert) : schluessel(schluessel), wert(wert) {}
// Getter für Schlüssel und Wert
SchluesselTyp getSchluessel() const
{
return schluessel;
}
WertTyp getWert() const
{
return wert;
}
// Setter für Schlüssel und Wert
void setSchluessel(SchluesselTyp neuerSchluessel)
{
schluessel = neuerSchluessel;
}
void setWert(WertTyp neuerWert)
{
wert = neuerWert;
}
// Überladen des < Operators
bool operator<(const schluesselWertPaar &paar) const
{
return schluessel < paar.getSchluessel();
}
};
template <typename SchluesselTyp, typename WertTyp>
void merge(std::vector<schluesselWertPaar<SchluesselTyp, WertTyp>> &vec, int start, int mid, int end)
{
int i, j, k;
int n1 = mid - start + 1;
int n2 = end - mid;
std::vector<schluesselWertPaar<SchluesselTyp, WertTyp>> left(n1), right(n2);
for (i = 0; i < n1; i++)
left[i] = vec[start + i];
for (j = 0; j < n2; j++)
right[j] = vec[mid + 1 + j];
i = 0;
j = 0;
k = start;
while (i < n1 && j < n2)
{
if (left[i] < right[j])
{
vec[k] = left[i];
i++;
}
else
{
vec[k] = right[j];
j++;
}
k++;
}
while (i < n1)
{
vec[k] = left[i];
i++;
k++;
}
while (j < n2)
{
vec[k] = right[j];
j++;
k++;
}
}
template <typename SchluesselTyp, typename WertTyp>
void mergeSort(std::vector<schluesselWertPaar<SchluesselTyp, WertTyp>> &vec, int start, int end)
{
if (start < end)
{
int mid = start + (end - start) / 2;
mergeSort(vec, start, mid);
mergeSort(vec, mid + 1, end);
merge(vec, start, mid, end);
}
}
template <typename SchluesselTyp, typename WertTyp>
int binarySearch(std::vector<schluesselWertPaar<SchluesselTyp, WertTyp>> &vec, int start, int end, SchluesselTyp key)
{
if (end >= start)
{
int mid = start + (end - start) / 2;
if (vec[mid].getSchluessel() == key)
return mid;
if (vec[mid].getSchluessel() > key)
return binarySearch(vec, start, mid - 1, key);
return binarySearch(vec, mid + 1, end, key);
}
return -1;
}
int main()
{
std::vector<schluesselWertPaar<int, std::string>> vec;
vec.push_back(schluesselWertPaar<int, std::string>(3, "drei"));
vec.push_back(schluesselWertPaar<int, std::string>(1, "eins"));
vec.push_back(schluesselWertPaar<int, std::string>(2, "zwei"));
std::cout << "Vor dem sortieren:\n";
for (const auto &paar : vec)
{
std::cout << paar.getSchluessel() << " " << paar.getWert() << std::endl;
}
mergeSort(vec, 0, vec.size() - 1);
std::cout << "Nach dem sortieren:\n";
for (const auto &paar : vec)
{
std::cout << paar.getSchluessel() << " " << paar.getWert() << std::endl;
}
int index = binarySearch(vec, 0, vec.size() - 1, 3);
if (index != -1)
{
std::cout << "Gefunden an Position: " << index + 1 << std::endl;
}
else
{
std::cout << "Nicht gefunden" << std::endl;
}
}
+149
View File
@@ -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;
}
};
```
+63
View File
@@ -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";
}
}
};
+134
View File
@@ -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;
}
+162
View File
@@ -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);
}
+18
View File
@@ -0,0 +1,18 @@
# README
## Specifikationen
Kommentiere die Funktionen mit:
- Voraussetzungen
- Ergebnis
- Seiteneffekte
## Abgabe
```markdown
Nachname1_Nachname2_UX_NRY
```
X = Nummer des **Übungszettels**
Y = Nummer der **Aufgabe**