Algorithmen & Datenstrukturen
Winter 2021/22
Meine Übungen am
  • Montag von 1300 bis 1430 Uhr (4. DS) im Raum APB/E009
  • Mittwoch von 1450 bis 1620 Uhr (5. DS) im Raum APB/E010
starten in der ersten Vorlesungswoche ab 11. Oktober 2021. Die Einschreibung findet via OPAL statt.

Inhalte der Übungen

Im Rahmen der zur Lehrveranstaltung gehörenden Übungen wenden wir das in der Vorlesung erlernte Wissen an und versuchen mit vielen Beispielen eine anschauliche Sicht auf die Thematik zu werfen. Dabei beschäftigen wir uns im ersten Teil mit Grundlagen der formalen Sprachtheorie und lernen zum Beispiel Syntaxdiagramme, den Rücksprungalgorithmus, EBNF und die Fixpunktsemantik kennen. Anschließend beschäftigen wir uns mit Grundlagen der Programmiersprache C, betrachten einfache Datenstrukturen und abschließend verkettete Listen und Bäume.

Im zweiten Teil behandeln wir grundlegende Algorithmen aus folgenden Themenbereichen:

  • Sortieren (Quicksort und Heapsort)
  • Suchen und Ersetzen (KMP-Algorithmus und Levenshtein-Dinstanz)
  • Graphalgorithmen (Breiten- und Tiefensuche, topologisches Sortieren, kürzeste Wege, algebraisches Pfadproblem)
  • EM-Algorithmus

News

  • 15.12.2021: Die Übungen um den Jahreswechsel herum müssen online abgehalten werden. Wir verwenden dafür Zoom - den immer gleich bleibenden Zugangslink findet ihr unten in den Materialien. Insbesondere wird auch der verschobene Mittwoch-Termin für das 10. Übungsblatt online stattfinden. Zusammenfassend sollte mit der folgenden, aktualisierten Übersicht alles klar werden.
    Montag-Übung:
    • 13.12.: Übungsblatt 10 - präsenz - wird vertreten
    • 20.12.: Übungsblatt 11 - online
    • Weihnachten
    •  
    • 10.01.: Übungsblatt 12 - präsenz (wenn möglich)
    Mittwoch-Übung:
    • 15.12.: keine Übung
    • 22.12.: Übungsblatt 10 - online
    • Weihnachten
    • 05.01.: Übungsblatt 11 - online
    • 12.01.: Übungsblatt 12 - präsenz (wenn möglich)
  • 01.12.2021: In der Woche vom 13.12. bis 17.12.2021 werde ich keine Übungen halten können. Die Montag-Übung in der 4. DS am 13.12. findet regulär statt und wird vertreten. Die Mittwoch-Übung in der 5. DS am 15.12. fällt aus und wird in der darauffolgenden Woche abgehalten. Damit verschiebt sich der Rhythmus der Mittwoch-Übung um eine Woche, was aber durch zwei Mittwoche um den Jahreswechsel ausgeglichen wird.
    Montag-Übung:
    • 06.12.: Übungsblatt 9
    • 13.12.: Übungsblatt 10 - wird vertreten
    • 20.12.: Übungsblatt 11
    • Weihnachten
    •  
    • 10.01.: Übungsblatt 12
    Mittwoch-Übung:
    • 08.12.: Übungsblatt 9
    • 15.12.: keine Übung
    • 22.12.: Übungsblatt 10
    • Weihnachten
    • 05.01.: Übungsblatt 11
    • 12.01.: Übungsblatt 12
  • 05.10.2021: Herzlich Willkommen im Kurs Algorithmen und Datenstrukturen im Wintersemester 2021/22. Die Übungen starten ab dem 11.10.2021 in Präsenz.
    • Montag von 1300 bis 1430 Uhr (4. DS)
    • Mittwoch von 1450 bis 1620 Uhr (5. DS)
    Die Einschreibung findet via OPAL statt.

Informationen

Alle für die Lehrveranstaltungen relevanten Informationen findet ihr im OPAL-Kurs. Die Vorlesung wird in diesem Semester als Video bereitgestellt und jeden Freitag im OPAL-Kurs verfügbar gemacht. In der folgenden Woche wird eine Auswahl an Aufgaben aus der Aufgabensammlung in den Übungen besprochen. Bei Fragen oder Problemen könnt ihr euch an mich oder an Thomas Ruprecht wenden. Mich erreicht ihr auch schnell und problemlos via Telegram @oakoneric.

Übungsmaterial

Vom Lehrstuhl werden die in der Vorlesung verwendeten Slides sowie eine Aufgabensammlung zur Verfügung gestellt. Diese Materialien sind nur aus dem TU-Netz erreichbar, d.h. ihr müsst dafür ggf. eine VPN-Verbindung verwenden. Für die verlinkten Materialien vom Lehrstuhl gelten die entsprechenden Hinweise im OPAL-Kurs.

Slides    Aufgabensammlung

Die in meinen Übungen verwendeten Materialien habe ich größtenteils in meiner Freizeit erstellt. Sie stammen insbesondere nicht vom Lehrstuhl und können damit trotz gründlicher Arbeit noch Fehler enthalten. Die Folien sollen keineswegs den aktiven Besuch der Übungen ersetzen, sondern vielmehr eine Hilfestellung sein um sich während der Übung auf das Verständnis konzentrieren zu können und das Nacharbeiten zu erleichtern. Die Slides und den zugehörigen Latex-Code dazu findet ihr in meinem entsprechenden Github-Repository. Findet ihr einen Fehler in meinen Lösungen, dann fühlt euch eingeladen mir dies mitzuteilen oder direkt selbst mit einem Request zu beheben.

Einen Eindruck der Übungen könnt ihr euch anhand des letzten Durchlaufes im Wintersemester 2020/21 machen. Damals musste ein großer Teil der Übungen online stattfinden - diese Übungen existieren auch als Video. Kontaktiert mich, wenn ihr die Zugangsdaten dafür benötigt.

Klausurvorbereitung

Zur Klausurvorbereitung findet ihr auf dem FTP-Server vom iFSR einige Altklausuren. Auch dafür benötigt ihr eine VPN-Verbindung ins Uni-Netz.

Übungsübersicht

Teil 1: Formale Sprachen

Woche 1: Einführung & Grundlagen der formalen Sprachen
11.10. bis 15.10.2021
Woche 2: Syntaxdiagramme & EBNF
18.10. bis 22.10.2021
Woche 3: Semantik von EBNF
25.10. bis 29.10.2021

Teil 2: Programmieren in C

Leider haben wir in den Übungen nicht die Zeit um allen einen funktionsfähigen Compiler und ein optimales Setup zu garantieren. Daher bitte ich euch die Informationen auf der Lehrveranstaltungswebsite zu beachten. Außerdem habe ich unter dem folgenden Button einige Hinweise zusammengetragen. Wie immer sind natürliche auch Google und YouTube sehr gute Quellen für weitere Hilfe. Im Zweifelsfall hilft euch sicher auch der iFSR.

Hinweise zum Programmieren mit C

Woche 4: Einführung in C
01.11. bis 05.11.2021
  • Übungsblatt 4
  • Lösungen
    • Aufgabe 1
      1. Maximum von zwei Zahlen:
        #include <stdio.h>
        int main() {
            int x, y, m;
            scanf("%d", &x);
            scanf("%d", &y);
            
            if ( x < y ) {
                m = y;
            } else { // x >= y
                m = x;
            }
            printf("Das Maximum von %d und %d ist %d.\n", x, y, m);
            return 0;
        }
      2. Fakultät \( n! = n * (n-1) * \dots * 2 * 1\)
        #include <stdio.h>
        
        int main(){
            int n, i, factorial = 1;
            scanf("%d", &n);
        
            for (i = 1; i <= n; i = i + 1){
                factorial = factorial * i;
            }
            
            printf("n! = %d\n", factorial);
        
            return 0;
        }
      3. Multiplikationstabelle
        #include <stdio.h>
        
        int main(){
            int i,j,n;
            scanf("%d", &n);
            
            for ( i=1; i<=n ; i=i+1 ) {
                for ( j=1; j<=n ; j=j+1 ) {
                    printf("%4d", i*j);
                }
                printf("\n");
            }
            return 0;
        }
      4. Primzahltest
        #include <stdio.h>
        
        int main(){
            for (int i = 2; i <= 1000; i++) {
                int j = 2, prime = 1;
                while (j*j <= i) {
                    if (i%j == 0) {
                    prime = 0;
                    break; // nicht notwendig
                    }
                j++;
                }
                if (prime == 1)
                printf("%d ist prim\n", i);
            }
        
            return 0;
        }
    • Aufgabe 2:
      #include <stdio.h>
      #include <stdlib.h>
      
      int main() {
      	unsigned int guess, number, n;
      
      	scanf("%u", &n);
      	
          number = 1 + rand() % n;
      	
          while(1) {
      		printf("Zahl raten: ");
      		scanf("%u", &guess);
      		if (guess == number) {
      			printf("Sehr gut!\n");
      			return 0;
      		} else if (guess > number) {
      			printf("Zahl ist kleiner!\n");
      		} else {
      			printf("Zahl ist groesser!\n");
      		}
      	}
      	
          return 0;
      }
Woche 5: Arrays und Funktionen
08.11. bis 12.11.2021
  • Übungsblatt 5
  • Lösungen
    • Aufgabe 1
      1. Ackermann-Funktion
        #include <stdio.h>
        
        int ack(int x, int y){
            if ((x == 0) && (y >= 0))
                return y + 1;
            else if ((x > 0) && (y == 0)) 
                return ack(x-1, 1);
            else if ((x > 0) && (y > 0)){
                return ack(x-1, ack(x, y-1));
            }
        }
        
        int main() {
            int x = 0, y = 0, a;
            printf("\nAckermannfunktion\n");
            printf("x = ");
            scanf("%d", &x);
            printf("y = ");
            scanf("%d", &y);
            
            a = ack(x,y);
            
            printf("ack(%i,%i)=%i.\n", x, y, a);
            
            return 0;
        }
      2. Prüfen eines Strings (Character-Array) auf die Palindrom-Eigenschaft
        #include <stdio.h>
        
        void palindrom(char feld[], int l, int *korrekt) {
            int i = 0;
            l = l - 1;
            *korrekt = 1;
            
            while(feld[l] == '\0')
                l = l - 1;
        
            while (i < l && *korrekt) {
                *korrekt = feld[i] == feld[l];
                i = i + 1;
                l = l - 1;
            }
        } // kein return wegen void
        
        int main() {
            #define ANZ 20
        
            char feld[ANZ];
            int korr;
        
            // Einlesen und Prüfen der Eingabe
            scanf("%[^\n]s", feld);
            printf("feld = %s\n",feld);
            
            // berechne Länge des Strings
            int len = 0;
            while(feld[len] != '\0')
                len++; 
        
            // teste Palindrom-Eigenschaft
            palindrom(feld, len, &korr);
        
            // drucke Palindrom-Eigenschaft aus
            if(korr == 1)
                printf("%s ist ein Palindrom.\n", feld);
            else
                printf("%s ist kein Plaindrom.\n", feld);
            
            return 0;
        }
    • Aufgabe 2:
      #include <stdio.h>
      
      void swap(int *a, int *b) {  /* hier weist der Stern auf Zeiger hin */
          int tmp;
      
          tmp = *a;
          *a = *b;
          *b = tmp;
          /* Der Stern vor einer Variablen bedeutet: Dereferenzieren
              nutze den Wert, der in der Adresse von x steht (insbes. nicht die Adresse)
          */
      }
      
      int main(){
          int x = 4, y = 6;
      
          printf("x = %d, y = %d \n", x, y);
          swap(&x, &y);
          /* Aufruf mit & vor einer Variablen gibt nur die Adresse 
             der Variablen weiter, nicht aber deren Wert 
             -> Änderung von globalen Variablen durch Funktionen möglich 
             (aber nicht schön - vermeiden)
          */
          printf("x = %d, y = %d \n", x, y);
      
          return 0;
      }
Woche 6: Listen und Bäume
15.11. bis 19.11.2021
  • Übungsblatt 6
  • Coding Templates
    • ausführliche Erklärung zur Notwendigkeit von Doppelreferenzen als [PDF] und im [Code]
    • Aufzeichnung der verschobenen Mittwoch-Übung
  • Lösungen
    • Aufgabe 1: Listen
      1. Anhängen an eine Liste
        void append(list *lp, int n)
        {
            while (*lp != NULL)
                lp = &((*lp)->next);
            /* gehe bis zum ende der liste */
        
            (*lp) = malloc(sizeof(struct element));
            /* erstelle ein neues element */
            (*lp)->value = n;
            /* fuelle den neuen container mit inhalt */
            (*lp)->next = NULL;
        }
      2. Summe einer Liste
        int sum_it(list l)
        {
            int result = 0;
        
            while (l != NULL)
            {
                result += l->value; /* result = result + l -> value */
                l = l->next;        /* schalte "start"zeiger ein element weiter */
            }
            return result;
        }
        
        int sum_rec(list l)
        {
            if (l == NULL)
                return 0;
            /* nach letztem element nichts mehr addieren */
        
            return l->value + sum_rec(l->next);
            /* nehme immer einen key und addiere die summe der restliste */
        }
      3. Entfernen aller gerade Listenelemente
        void rmEvens_rec(list *lp)
        { /* *lp heißt: wir übergeben einen pointer (list pointer) */
            if (lp == NULL || *lp == NULL)
                return;
            /* keine liste oder liste leer */
        
            if ((*lp)->value % 2 == 0)
            {
                list tmp = *lp;
                /* speicherung des zu löschenden elements (um speicher dann zu befreien, sonst speicherleichen) */
                *lp = (*lp)->next;
                /* weiterschalten des ersten pointers */
                free(tmp);
                /* speicher des zu löschenden elements befreien */
            }
            else
            {
                lp = &(*lp)->next;
                /* startpointer weiterschalten - kein löschen notwendig */
            }
        
            rmEvens_rec(lp); /* verfahre so weiter mit der restlichen liste */
        }
        
        void rmEvens_it(list *lp)
        {
            while (lp != NULL && *lp != NULL)
            {
                if ((*lp)->value % 2 == 0)
                {
                    list tmp = *lp;
                    *lp = (*lp)->next;
                    free(tmp);
                }
                else
                    lp = &(*lp)->next;
            }
        }
    • Aufgabe 2: Bäume
      1. Erstellen eines neuen Knotens
        tree createNode(int n, tree l, tree r)
        {
            tree t = malloc(sizeof(struct node));
            t->left = l;
            t->right = r;
            t->key = n;
            return t;
        }
      2. Einfügen eines Knotens am linkesten Blatt
        void insertl(tree *tp, int n)
        {
            if (*tp != NULL)
                insertl(&((*tp)->left), n);
            else
                *tp = createNode(n, NULL, NULL);
        }
      3. Produkt der Blätter
        int leafprod(tree t)
        {
            if (t == NULL)
                return 1;
        
            if (t->left == NULL && t->right == NULL)
                return t->key;
        
            return leafprod(t->left) * leafprod(t->right);
        }
      4. Baum zu Liste umwandeln (nutzt append aus Aufgabe 1)
        void treeToList_rec(tree t, list *lp)
        {
            if (t == NULL)
                return;
            treeToList_rec(t->left, lp);
            if (t->key % 2 == 0)
                append(lp, t->key);
            treeToList_rec(t->right, lp);
        }

Teil 3: Algorithmen

Woche 7: Sortieren
22.11. bis 26.11.2021
Woche 8: Suchen & Ersetzen
29.11. bis 03.12.2021
Woche 9: AVL-Bäume & Topologisches Sortieren
06.12. bis 10.12.2021
  • Übungsblatt 9
  • Coding Templates
  • Lösungen
    • Aufgabe 2: Implementierungen für AVL-Bäume
      1. Ermitteln der Balancefaktoren
        int height(tree t)
        {
            if (t == NULL)
                return 0;
        
            int hl = height(t->left);
            int hr = height(t->right);
        
            return (hl > hr) ? hl + 1 : hr + 1;
        }
        
        tree bfs(tree t)
        {
            if (t == NULL)
                return NULL;
            tree bf = malloc(sizeof(struct node));
            bf->key = height(t->right) - height(t->left);
            bf->left = bfs(t->left);
            bf->right = bfs(t->right);
            return bf;
        }
      2. Linksrotation um die Wurzel
        void lRot(tree *tp)
        {
            if (tp == NULL || *tp == NULL)
                return;
        
            tree rightChild = (*tp)->right;
            (*tp)->right = rightChild->left;
            rightChild->left = *tp;
            *tp = rightChild;
        }
Woche 10: Topologisches Sortieren & Graphensuche
13.12. bis 17.12.2021
  • Die Mittwoch-Übung am 15.12.2021 findet nicht statt! Das Übungsblatt 10 wird am Mittwoch, dem 22.12.2021 in einer Online-Übung besprochen (siehe oben). Wir verwenden dafür wie bereits erprobt Zoom. Den Link findet ihr unten.
  • Vorlesungsvideo: Graphensuche
  • Übungsblatt 10
  • zum Meeting am 22.12.2021
  • Coding Templates
  • Lösungen Aufgabe 1: Implementierung des Topologischen Sortierens
    void topsort(int n, int e, struct Edge edges[], int ord[])
    {
        int j = 1, node, edge, ok;
        while (j <= n)
        {
            for (node = 0; node < n; ++node)
            {
                if (ord[node] == -1)
                {
                    ok = 1;
                    for (edge = 0; edge < e; ++edge)
                        if (edges[edge].to == node && ord[edges[edge].from] == -1)
                            ok = 0;
                    if (ok)
                    {
                        printf("Knoten %d bekommt Nummer %d. \n", node, j);
                        ord[node] = j;
                        j++;
                        break;
                    }
                }
            }
        }
    }
Woche 11: Kürzeste Wege
20.12.2021 bis 07.01.2022
  • Die Übungen zum 11. Übungsblatt finden am Montag, dem 20.12.2021 ab 1300 Uhr und am Mittwoch, dem 05.01.2022 um 1450 Uhr statt.
  • Die Übungen um den Jahreswechsel herum müssen digital abgehalten werden. Aufgrund meiner positiven Erfahrungen werden wir Zoom verwenden. Ihr erreicht das Meeting über den unten stehenden Button (der Link bleibt stets gleich).
  • Vorlesungsvideo: Floyd-Warshall-Algorithmus
  • Übungsblatt 11
  • zum Meeting
Woche 12: Algebraisches Pfadproblem
10.01. bis 14.01.2022
  • Die Übungen zum 12. Übungsblatt können nach derzeitigem Stand in Präsenz durchgeführt werden. Sollte sich daran etwas Grundlegendes ändern, werde ich euch informieren. Wenn ihr die Übung lieber online abhalten möchtet, dann schreibt mir gern.
  • Übungsblatt 12
    • Ich habe einige theoretische Fakten, die zum tieferen Verständnis des algebraischen Pfadproblems nützlich sind, hier zusammengetragen. Das kann vor allem für den Beweis in Aufgabe 1c hilfreich sein.
Woche 13: Wahrscheinlichkeitstheorie
17.01. bis 21.01.2022
Woche 14: EM-Algorithmus
24.01. bis 28.01.2022
  • Da in diesem Semester für die AuD-Übungen keine offizielle Lehrveranstaltungsevaluation durchgeführt wird, habe ich eine inoffizielle Umfrage erstellt. Diese erreicht ihr über den unten stehenden Button. Ich würde mich sehr über euer Feedback freuen - dieses wird euch sicher in den Übungen zur Programmierung im kommenden Sommersemester zu Gute kommen.
  • Übungsblatt 14
    • Hier findet ihr eine ausführliche Erklärung zur Aufgabe 2. Die Erklärung stammt aus dem letzten Jahr, weswegen sie noch mit Aufgabe 1 überschrieben ist.
    • Wer Spaß am EM-Algorithmus hat: ich habe hier das Beispiel aus der Vorlesung in einem JupyterNotebook nachimplementiert. Insbesondere könnt ihr die Likelihood-Funktion geplottet sehen und wie die einzelnen EM-Schritte sich dem Maximum nähern.
Woche 15: Wiederholung & Konsultation
31.01. bis 04.02.2022
  • Da in diesem Semester für die AuD-Übungen keine offizielle Lehrveranstaltungsevaluation durchgeführt wird, habe ich eine inoffizielle Umfrage erstellt. Diese erreicht ihr über den unten stehenden Button. Ich würde mich sehr über euer Feedback freuen - dieses wird euch sicher in den Übungen zur Programmierung im kommenden Sommersemester zu Gute kommen.
  • Für diese Übung gibt es kein offizielles Übungsblatt. Wir nutzen die Übung um über eure offenen Fragen zu sprechen. Dazu können wir die Zusatzaufgaben der Übungsblätter sowie auch alte Klausuren aus der Aufgabensammlung oder vom FTP-Server des iFSR (dafür benötigt ihr eine VPN-Verbindung ins Uni-Netz) nutzen.
  • Solltet ihr bereits konkrete Wünsche für einzelne Aufgaben oder Themen haben, dann sendet mir diese bitte bis zum Abend vor der Übung zu. Dann kann ich die Übung besser vorbereiten und ihr sicher mehr davon profitieren.