diff --git a/20190121/aufgabe-2.c b/20190121/aufgabe-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..f26ce5d665012e2cdbaac8b0f43f01d40f338254
--- /dev/null
+++ b/20190121/aufgabe-2.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct node
+{
+  int content;
+  struct node *next;
+} node;
+
+void output_list (node *first)
+{
+  for (node *p = first; p; p = p->next)
+    printf ("%d ", p->content);
+  printf ("\n");
+}
+
+void insert_into_list (node *what, node *where)
+{
+  what->next = where->next;
+  where->next = what;
+}
+
+int main (void)
+{
+  node *element3 = malloc (sizeof (node));
+  node *element7 = malloc (sizeof (node));
+  node *element137 = malloc (sizeof (node));
+
+  element3->content = 3;
+  element7->content = 7;
+  element137->content = 137;
+
+  node *first = element3;
+  element3->next = element7;
+  element7->next = element137;
+  element137->next = NULL;
+
+  output_list (first);
+
+  node *element5 = malloc (sizeof (node));
+  element5->content = 5;
+  insert_into_list (element5, element3);
+
+  output_list (first);
+
+  return 0;
+}
diff --git a/20190121/hp-musterloesung-20190121.pdf b/20190121/hp-musterloesung-20190121.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..57b065d7ede6125a1452a0021a7f49b6a62997ef
Binary files /dev/null and b/20190121/hp-musterloesung-20190121.pdf differ
diff --git a/20190121/hp-musterloesung-20190121.tex b/20190121/hp-musterloesung-20190121.tex
new file mode 100644
index 0000000000000000000000000000000000000000..12bce3992a5fb928b5fdfb415c9838dd6a870cea
--- /dev/null
+++ b/20190121/hp-musterloesung-20190121.tex
@@ -0,0 +1,598 @@
+% hp-musterloesung-20190121.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
+% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019  Peter Gerwinski
+%
+% This document is free software: you can redistribute it and/or
+% modify it either under the terms of the Creative Commons
+% Attribution-ShareAlike 3.0 License, or under the terms of the
+% GNU General Public License as published by the Free Software
+% Foundation, either version 3 of the License, or (at your option)
+% any later version.
+%
+% This document is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with this document.  If not, see <http://www.gnu.org/licenses/>.
+%
+% You should have received a copy of the Creative Commons
+% Attribution-ShareAlike 3.0 Unported License along with this
+% document.  If not, see <http://creativecommons.org/licenses/>.
+
+% README: Stack-Operationen, einfach und doppelt verkettete Listen, ternärer Baum
+
+\documentclass[a4paper]{article}
+
+\usepackage{pgscript}
+\usepackage{tikz}
+
+\begin{document}
+
+  \section*{Hardwarenahe Programmierung\\
+            Musterlösung zu den Übungsaufgaben -- 21.\ Januar 2019}
+
+  \exercise{Stack-Operationen}
+
+  Wir betrachten das folgende Programm (\gitfile{hp}{20190121}{aufgabe-1.c}):
+
+  \begin{minipage}[t]{0.5\textwidth}
+    \begin{lstlisting}[gobble=6]
+      #include <stdio.h>
+
+      #define STACK_SIZE 10
+
+      int stack[STACK_SIZE];
+      int stack_pointer = 0;
+
+      void push (int x)
+      {
+        stack[stack_pointer++] = x;
+      }
+
+      int pop (void)
+      {
+        return stack[--stack_pointer];
+      }
+
+      void show (void)
+      {
+        printf ("stack content:");
+        for (int i = 0; i < stack_pointer; i++)
+          printf (" %d", stack[i]);
+        if (stack_pointer)
+          printf ("\n");
+        else
+          printf (" (empty)\n");
+      }
+    \end{lstlisting}
+  \end{minipage}\hfill
+  \begin{minipage}[t]{0.5\textwidth}
+    \begin{lstlisting}[gobble=6]
+      void insert (int x, int pos)
+      {
+        for (int i = pos; i < stack_pointer; i++)
+          stack[i + 1] = stack[i];
+        stack[pos] = x;
+        stack_pointer++;
+      }
+
+      void insert_sorted (int x)
+      {
+        int i = 0;
+        while (i < stack_pointer && x < stack[i])
+          i++;
+        insert (x, i);
+      }
+
+      int main (void)
+      {
+        push (3);
+        push (7);
+        push (137);
+        show ();
+        insert (5, 1);
+        show ();
+        insert_sorted (42);
+        show ();
+        insert_sorted (2);
+        show ();
+        return 0;
+      }
+    \end{lstlisting}
+  \end{minipage}
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      Ändern Sie das Programm so,
+      daß die Funktion \lstinline{insert()} ihren Parameter \lstinline{x}
+      an der Stelle \lstinline{pos} in den Stack einfügt,
+      und den sonstigen Inhalt des Stacks verschiebt, aber nicht zerstört.
+      \points{3}
+    \item
+      Ändern Sie das Programm so,
+      daß die Funktion \lstinline{insert_sorted()} ihren Parameter \lstinline{x}
+      an derjenigen Stelle einfügt, an die er von der Sortierung her gehört.
+      (Der Stack wird hierbei vor dem Funktionsaufruf als sortiert vorausgesetzt.)
+      \points{2}
+    \item
+      Schreiben Sie eine zusätzliche Funktion \lstinline{int search (int x)},
+      die die Position (Index) des Elements \lstinline{x}
+      innerhalb des Stack zurückgibt
+      -- oder \lstinline{-1}, wenn \lstinline{x} nicht im Stack enthalten ist.
+      Der Rechenaufwand darf höchstens $\mathcal{O}(n)$ betragen.
+      \points{3}
+    \item
+      Wie (c), aber der Rechenaufwand darf höchstens $\mathcal{O}(\log n)$ betragen.
+      \points{4}
+  \end{enumerate}
+
+  \solution
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      \textbf{Ändern Sie das Programm so,
+      daß die Funktion \lstinline{insert()} ihren Parameter \lstinline{x}
+      an der Stelle \lstinline{pos} in den Stack einfügt,
+      und den sonstigen Inhalt des Stacks verschiebt, aber nicht zerstört.}
+
+      Die \lstinline{for}-Schleife in der  Funktion \lstinline{insert()}
+      durchläuft das Array von unten nach oben.
+      Um den Inhalt des Arrays von unten nach oben zu verschieben,
+      muß man die Schleife jedoch von oben nach unten durchlaufen.
+
+      \goodbreak
+      Um die Funktion zu reparieren, ersetze man also
+      \begin{lstlisting}[gobble=8]
+        for (int i = pos; i < stack_pointer; i++)
+      \end{lstlisting}
+      durch:
+      \begin{lstlisting}[gobble=8]
+        for (int i = stack_pointer - 1; i >= pos; i--)
+      \end{lstlisting}
+      (Siehe auch: \gitfile{hp}{20190121}{loesung-1.c})
+
+    \item
+      \textbf{Ändern Sie das Programm so,
+      daß die Funktion \lstinline{insert_sorted()} ihren Parameter \lstinline{x}
+      an derjenigen Stelle einfügt, an die er von der Sortierung her gehört.
+      (Der Stack wird hierbei vor dem Funktionsaufruf als sortiert vorausgesetzt.)}
+
+      Der Vergleich \lstinline{x < stack[i]}
+      als Bestandteil der \lstinline{while}-Bedingung
+      paßt nicht zur Durchlaufrichtung der Schleife (von unten nach oben).
+
+      Um die Funktion zu reparieren, kann man daher entweder
+      das Kleinerzeichen durch ein Größerzeichen ersetzen
+      (\lstinline{x > stack[i]} -- siehe \gitfile{hp}{20190121}{loesung-1b-1.c})
+      oder die Schleife von oben nach unten durchlaufen
+      (siehe \gitfile{hp}{20190121}{loesung-1b-2.c}).
+
+      Eine weitere Möglichkeit besteht darin,
+      das Suchen nach der Einfügeposition
+      mit dem Verschieben des Arrays zu kombinieren
+      (siehe \gitfile{hp}{20190121}{loesung-1.c}).
+      Hierdurch spart man sich eine Schleife; das Programm wird schneller.
+      (Es bleibt allerdings bei $\mathcal{O}(n)$.)
+
+    \item
+      \textbf{Schreiben Sie eine zusätzliche Funktion \lstinline{int search (int x)},
+      die die Position (Index) des Elements \lstinline{x}
+      innerhalb des Stack zurückgibt
+      -- oder \lstinline{-1}, wenn \lstinline{x} nicht im Stack enthalten ist.
+      Der Rechenaufwand darf höchstens $\mathcal{O}(n)$ betragen.}
+
+      Man geht in einer Schleife den Stack (= den genutzten Teil des Arrays) durch.
+      Bei Gleichheit gibt man direkt mit \lstinline{return} den Index zurück.
+      Nach dem Schleifendurchlauf steht fest,
+      daß \lstinline{x} nicht im Stack vorhanden ist;
+      man kann dann direkt \lstinline{-1} zurückgeben
+      (siehe \gitfile{hp}{20190121}{loesung-1c.c}).
+
+      Da es sich um eine einzelne Schleife handelt,
+      ist die Ordnung $\mathcal{O}(n)$.
+
+    \item
+      \textbf{Wie (c), aber der Rechenaufwand darf höchstens $\mathcal{O}(\log n)$ betragen.}
+
+      Um $\mathcal{O}(\log n)$ zu erreichen,
+      halbiert man fortwährend das Intervall von (einschließlich) \lstinline{0}
+      bis (ausschließlich) \lstinline{stack_pointer}
+      (siehe \gitfile{hp}{20190121}{loesung-1d.c}) --
+      wie in der Funktion \lstinline{push_sorted()}
+      im Beispiel-Programm \gitfile{hp}{20190121}{stack-11.c}.
+
+      Ein wichtiger Unterschied besteht darin,
+      daß man nach dem Durchlauf der Schleife noch auf die Gleichheit
+      \lstinline{x == stack[left]} (insbesondere nicht: \lstinline{stack[right]})
+      prüfen und ggf.\ \lstinline{left} bzw.\ \lstinline{-1} zurückgeben muß.
+  \end{enumerate}
+
+  \goodbreak
+
+  \exercise{Einfach und doppelt verkettete Listen}
+
+  Das Beispiel-Programm \gitfile{hp}{20190121}{aufgabe-2.c}
+  demonstriert zwei Funktionen zur Verwaltung einfach verketteter Listen:
+  \lstinline{output_list()} zum Ausgeben der Liste auf den Bildschirm und
+  \lstinline{insert_into_list()} zum Einfügen in die Liste.
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      Ergänzen Sie eine Funktion \lstinline{delete_from_list()}
+      zum Löschen eines Elements aus der Liste
+      mit Freigabe des Speicherplatzes.
+      \points{5}
+    \item
+      Ergänzen Sie eine Funktion \lstinline{reverse_list()}
+      die die Reihenfolge der Elemente in der Liste umdreht.\\
+      \points{3}
+  \end{enumerate}
+
+  Eine doppelt verkettete Liste hat in jedem Knotenpunkt (\lstinline{node})
+  \emph{zwei\/} Zeiger -- einen auf das nächste Element (\lstinline{next})
+  und einen auf das vorherige Element (z.\,B.\ \lstinline{prev} für "`previous"').
+  Dadurch ist es leichter als bei einer einfach verketteten Liste,
+  die Liste in umgekehrter Reihenfolge durchzugehen.
+
+  \begin{quote}
+    \begin{tikzpicture}
+      \color{blendedblue}
+      \node(first) at (0,3.5) {first};
+      \node(NULL1) at (-1,1.25) {NULL};
+      \node[shape=rectangle,draw,line width=1pt](3) at (1,2) {3};
+      \node[shape=rectangle,draw,line width=1pt](7) at (3,2) {7};
+      \node[shape=rectangle,draw,line width=1pt](137) at (5,2) {137};
+      \node(NULL2) at (7,2.75) {NULL};
+      \draw[-latex](first)--(3);
+      \draw[-latex](3) to[out=45,in=135] (7);
+      \draw[-latex](3) to[out=-135,in=0] (NULL1);
+      \draw[-latex](7) to[out=-135,in=-45] (3);
+      \draw[-latex](7) to[out=45,in=135] (137);
+      \draw[-latex](137) to[out=-135,in=-45] (7);
+      \draw[-latex](137) to[out=45,in=180] (NULL2);
+    \end{tikzpicture}
+  \end{quote}
+
+  Der Rückwärts-Zeiger (\lstinline{prev}) des ersten Elements zeigt,
+  genau wie der Vorwärts-Zeiger (\lstinline{next}) des letzten Elements,
+  auf \emph{nichts}, hat also den Wert \lstinline{NULL}.
+
+  \begin{enumerate}[\quad(a)]\setcounter{enumi}{2}
+    \item
+      Schreiben Sie das Programm um für doppelt verkettete Listen.
+      \points{5}
+  \end{enumerate}
+
+  \solution
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      \textbf{Ergänzen Sie eine Funktion \lstinline{delete_from_list()}
+      zum Löschen eines Elements aus der Liste
+      mit Freigabe des Speicherplatzes.}
+
+      Siehe: \gitfile{hp}{20190121}{loesung-2a.c}
+
+      Um ein Element aus einer verketteten Liste zu löschen,
+      müssen zuerst die Zeiger umgestellt werden,
+      um das Element von der Liste auszuschließen.
+      Erst danach darf der Speicherplatz für das Element freigegeben werden.
+
+%      Da das Beispielprogramm (\gitfile{hp}{20190121}{lists-5.c})
+%      nicht mit dynamischem Speicher arbeitet,
+%      stellen wir dieses zunächst auf dynamischen Speicher um, z.\,B.:
+%      \begin{lstlisting}[gobble=8]
+%        node *element5 = malloc (sizeof (node));
+%      \end{lstlisting}
+%      Danach bezeichnet \lstinline{element5}
+%      die Adresse der \lstinline{struct}-Variablen;
+%      es wird also \lstinline{element5} an die Funktionen übergeben
+%      und nicht \lstinline{&element5} (die Adresse des Zeigers).
+
+%      Um nun ein Element aus der Liste zu entfernen,
+%      benötigt man
+      Man benötigt also
+      \emph{das vorherige Element},
+      dessen \lstinline{next}-Zeiger man dann auf
+      das übernächste Element \lstinline{next->next} weitersetzt.
+
+      Bei jedem Zeiger muß man vor dem Zugriff prüfen,
+      daß dieser nicht auf \lstinline{NULL} zeigt.
+      (Die Musterlösung ist in dieser Hinsicht nicht konsequent.
+      Für den Produktiveinsatz müßte z.\,B.\ \lstinline{delete_from_list()}
+      auch den übergebenen Zeiger \lstinline{what} auf \lstinline{NULL} prüfen.)
+
+      Ein Spezialfall tritt ein, wenn das erste Element einer Liste
+      entfernt werden soll. In diesem Fall tritt \lstinline{first}
+      an die Stelle des \lstinline{next}-Zeigers
+      des (nicht vorhandenen) vorherigen Elements.
+      Da \lstinline{delete_from_list()} \emph{schreibend\/} auf \lstinline{first}
+      zugreift, muß \lstinline{first} \emph{als Zeiger\/} übergeben werden
+      (\lstinline{node **first}).
+
+      Um alle Spezialfälle zu testen (am Anfang, am Ende und in der Mitte der Liste),
+      wurden die Testfälle im Hauptprogramm erweitert.
+
+    \item
+      \textbf{Schreiben Sie das Programm um für doppelt verkettete Listen.}
+
+      Siehe: \gitfile{hp}{20190121}{loesung-2b.c}
+
+      Bei allen Einfüge- und Löschaktionen müssen \emph{jeweils zwei\/}
+      \lstinline{next}- und \lstinline{prev}-Zeiger neu gesetzt werden.
+
+      Zum Debuggen empfiehlt es sich sehr,
+      eine Funktion zu schreiben, die die Liste auf Konsistenz prüft
+      (hier: \lstinline{check_list()}).
+
+      Das Testprogramm macht von der Eigenschaft der doppelt verketteten Liste,
+      daß man sie auch rückwärts effizient durchgehen kann, keinen Gebrauch.
+      Um diese Eigenschaft als Vorteil nutzen zu können, empfiehlt es sich,
+      zusätzlich zu \lstinline{first}
+      auch einen Zeiger auf das letzte Element (z.\,B.\ \lstinline{last})
+      einzuführen. Dieser muß dann natürlich bei allen Operationen
+      (Einfügen, Löschen, \dots) auf dem aktuellen Stand gehalten werden.
+
+  \end{enumerate}
+
+  \exercise{Ternärer Baum}
+
+  Der in der Vorlesung vorgestellte \newterm{binäre Baum\/}
+  ist nur ein Spezialfall;
+  im allgemeinen können Bäume auch mehr als zwei Verzweigungen
+  pro Knotenpunkt haben.
+  Dies ist nützlich bei der Konstruktion \emph{balancierter Bäume},
+  also solcher, die auch im \emph{Worst Case\/}
+  nicht zu einer linearen Liste entarten,
+  sondern stets eine -- möglichst flache -- Baumstruktur behalten.
+
+  Wir betrachten einen Baum mit bis zu drei Verzweigungen pro Knotenpunkt,
+  einen sog.\ \newterm{ternären Baum}.
+  Jeder Knoten enthält dann nicht nur einen,
+  sondern \emph{zwei\/} Werte als Inhalt:
+
+  \begin{lstlisting}
+    typedef struct node
+    {
+      int content_left, content_right;
+      struct node *left, *middle, *right;
+    } node;
+  \end{lstlisting}
+
+  Wir konstruieren nun einen Baum nach folgenden Regeln:
+  \vspace{-\medskipamount}
+  \begin{itemize}\itemsep0pt
+    \item
+      Innerhalb eines Knotens sind die Werte sortiert:
+      \lstinline{content_left} muß stets kleiner sein als \lstinline{content_right}.
+    \item
+      Der Zeiger \lstinline{left} zeigt auf Knoten,
+      deren enthaltene Werte durchweg kleiner sind als \lstinline{content_left}.
+    \item
+      Der Zeiger \lstinline{right} zeigt auf Knoten,
+      deren enthaltene Werte durchweg größer sind als \lstinline{content_right}.
+    \item
+      Der Zeiger \lstinline{middle} zeigt auf Knoten,
+      deren enthaltene Werte durchweg größer sind als \lstinline{content_left},
+      aber kleiner als \lstinline{content_right}.
+    \item
+      Ein Knoten muß nicht immer mit zwei Werten voll besetzt sein;
+      er darf auch \emph{nur einen\/} gültigen Wert enthalten.
+
+      Der Einfachheit halber lassen wir in diesem Beispiel
+      nur positive Zahlen als Werte zu.
+      Wenn ein Knoten nur einen Wert enthält,
+      setzen wir \lstinline{content_right = -1},
+      und der Zeiger \lstinline{middle} wird nicht verwendet.
+    \item
+      Wenn wir neue Werte in den Baum einfügen,
+      werden \emph{zuerst\/} die nicht voll besetzten Knoten aufgefüllt
+      und \emph{danach erst\/} neue Knoten angelegt und Zeiger gesetzt.
+    \item
+      Beim Auffüllen eines Knotens darf nötigenfalls \lstinline{content_left}
+      nach \lstinline{content_right} verschoben werden.
+      Ansonsten werden einmal angelegte Knoten nicht mehr verändert.
+  \end{itemize}
+  \vspace*{-\medskipamount}
+  (In der Praxis dürfen Knoten gemäß speziellen Regeln
+  nachträglich verändert werden,
+  um Entartungen gar nicht erst entstehen zu lassen --
+  siehe z.\,B.\ \url{https://de.wikipedia.org/wiki/2-3-4-Baum}.)
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      Zeichnen Sie ein Schaubild, das veranschaulicht,
+      wie die Zahlen 7, 137, 3, 5, 6, 42, 1, 2 und 12
+      nacheinander und in dieser Reihenfolge
+      in den oben beschriebenen Baum eingefügt werden
+      -- analog zu den Vortragsfolien (\gitfile{hp}{20190121}{hp-20190121.pdf}),
+      Seite 43.
+      \points{3}
+      %
+      % Lösung:
+      %
+      %           7 137
+      %         /   |
+      %      3 5  12 42
+      %     /   \
+      %   1 2    6
+      %
+      % (NULL-Zeiger sind hier nicht dargestellt,
+      % gehören aber dazu.)
+      %
+    \goodbreak
+    \item
+      Dasselbe, aber in der Reihenfolge 
+      2, 7, 42, 12, 1, 137, 5, 6, 3.
+      \points{3}
+      %
+      % Lösung:
+      %
+      %           2 7
+      %         /  |  \
+      %       1   5 6  12 42
+      %         /           \
+      %        3             137
+      %
+      % (NULL-Zeiger sind hier wieder nicht dargestellt,
+      % gehören aber dazu.)
+      %
+    \item
+      Beschreiben Sie in Worten und/oder als C-Quelltext-Fragment,
+      wie eine Funktion aussehen müßte, um den auf diese Weise entstandenen Baum
+      sortiert auszugeben.
+      \points{4}
+  \end{enumerate}
+
+  \solution
+
+  \begin{enumerate}[\quad(a)]
+    \item
+      \textbf{Zeichnen Sie ein Schaubild, das veranschaulicht,
+      wie die Zahlen 7, 137, 3, 5, 6, 42, 1, 2 und 12
+      nacheinander und in dieser Reihenfolge
+      in den oben beschriebenen Baum eingefügt werden
+      -- analog zu den Vortragsfolien (\gitfile{hp}{20190121}{hp-20190121.pdf}),
+      Seite 21.}
+      %
+      % Lösung:
+      %
+      %           7 137
+      %         /   |
+      %      3 5  12 42
+      %     /   \
+      %   1 2    6
+      %
+      % (NULL-Zeiger sind hier nicht dargestellt,
+      % gehören aber dazu.)
+      %
+      \begin{center}
+        \newcommand{\x}{~\makebox(0,0){\raisebox{0.7em}{\rule{1pt}{1.4em}}}~}
+        \begin{tikzpicture}
+          \color{blendedblue}
+          \node(root) at (0,0) {\lstinline{node *root;}};
+          \node[shape=rectangle,draw,line width=1pt](7-137) at (0,-1.5) {7\x 137};
+          \draw[-latex](root)--(7-137);
+          \node[shape=rectangle,draw,line width=1pt](3-5) at (-2,-3) {3\x 5};
+          \draw[-latex](7-137)--(3-5);
+          \node[shape=rectangle,draw,line width=1pt](12-42) at (0,-3) {12\x 42};
+          \draw[-latex](7-137)--(12-42);
+          \node[shape=rectangle,draw,line width=1pt](1-2) at (-4,-4.5) {1\x 2};
+          \draw[-latex](3-5)--(1-2);
+          \node[shape=rectangle,draw,line width=1pt](6) at (0,-4.5) {6};
+          \draw[-latex](3-5)--(6);
+        \end{tikzpicture}
+      \end{center}
+      Bemerkungen:
+      \begin{itemize}
+        \item
+          Zeiger mit dem Wert \lstinline{NULL} sind nicht dargestellt:
+          \lstinline{right}-Zeiger von 7/137,
+          \lstinline{middle}-Zeiger von 3/5,
+          sämtliche Zeiger von 1/2, 12/42 und 6.
+        \item
+          Beim Einfügen der 12 wird die sich bereits vorher in diesem
+          \lstinline{node} befindliche 42 zu \lstinline{content_right},
+          und die 12 wird das neue \lstinline{content_left}.
+        \item
+          Dieser Baum hat sehr einfache Regeln und ist daher \emph{nicht\/}
+          balanciert. Insbesondere unsere Regel, daß einmal angelegte Knoten
+          nicht mehr verändert werden dürfen, steht dem im Wege.
+          Ein einfaches Beispiel für einen \emph{balancierten\/} ternären
+          Baum ist der 2-3-Baum -- siehe z.\,B.\ \url{https://en.wikipedia.org/wiki/2-3_tree}.
+      \end{itemize}
+    \goodbreak
+    \item
+      \textbf{Dasselbe, aber in der Reihenfolge 
+      2, 7, 42, 12, 1, 137, 5, 6, 3.}
+      %
+      % Lösung:
+      %
+      %           2 7
+      %         /  |  \
+      %       1   5 6  12 42
+      %         /           \
+      %        3             137
+      %
+      % (NULL-Zeiger sind hier wieder nicht dargestellt,
+      % gehören aber dazu.)
+      %
+      \begin{center}
+        \newcommand{\x}{~\makebox(0,0){\raisebox{0.7em}{\rule{1pt}{1.4em}}}~}
+        \begin{tikzpicture}
+          \color{blendedblue}
+          \node(root) at (0,0) {\lstinline{node *root;}};
+          \node[shape=rectangle,draw,line width=1pt](2-7) at (0,-1.5) {2\x 7};
+          \draw[-latex](root)--(7-137);
+          \node[shape=rectangle,draw,line width=1pt](1) at (-2,-3) {1};
+          \draw[-latex](2-7)--(1);
+          \node[shape=rectangle,draw,line width=1pt](5-6) at (0,-3) {5\x 6};
+          \draw[-latex](2-7)--(5-6);
+          \node[shape=rectangle,draw,line width=1pt](3) at (-2,-4.5) {3};
+          \draw[-latex](5-6)--(3);
+          \node[shape=rectangle,draw,line width=1pt](12-42) at (2,-3) {12\x 42};
+          \draw[-latex](2-7)--(12-42);
+          \node[shape=rectangle,draw,line width=1pt](137) at (4,-4.5) {137};
+          \draw[-latex](12-42)--(137);
+        \end{tikzpicture}
+      \end{center}
+      Bemerkungen:
+      \begin{itemize}
+        \item
+          Wieder sind Zeiger mit dem Wert \lstinline{NULL} nicht dargestellt:
+          \lstinline{middle}- und \lstinline{right}-Zeiger von 5/6,
+          \lstinline{left}- und \lstinline{middle}-Zeiger von 12/42,
+          sämtliche Zeiger von 1, 3 und 137.
+        \item
+          Beim Einfügen der 12 wird wieder die sich bereits vorher in diesem
+          \lstinline{node} befindliche 42 zu \lstinline{content_right},
+          und die 12 wird das neue \lstinline{content_left}.
+      \end{itemize}
+    \item
+      \textbf{Beschreiben Sie in Worten und/oder als C-Quelltext-Fragment,
+      wie eine Funktion aussehen müßte, um den auf diese Weise entstandenen Baum
+      sortiert auszugeben.}
+
+      Die entscheidende Idee ist \textbf{Rekursion}.
+
+      Eine Funktion, die den gesamten Baum ausgibt,
+      müßte einmalig für den Zeiger \lstinline{root} aufgerufen werden
+      und folgendes tun:
+      \begin{enumerate}[\quad 1.]
+        \item
+          falls der übergebene Zeiger den Wert \lstinline{NULL} hat,
+          nichts ausgeben, sondern die Funktion direkt beenden,
+        \item
+          sich selbst für den \lstinline{left}-Zeiger aufrufen,
+        \item
+          den Wert von \lstinline{content_left} ausgeben,
+        \item
+          sich selbst für den \lstinline{middle}-Zeiger aufrufen,
+        \item
+          sofern vorhanden (also ungleich \lstinline{-1}),
+          den Wert von \lstinline{content_right} ausgeben,
+        \item
+          sich selbst für den \lstinline{right}-Zeiger aufrufen.
+      \end{enumerate}
+      Als C-Fragment:
+      \begin{lstlisting}[gobble=8]
+        void output_tree (node *root)
+        {
+          if (root)
+            {
+              output_tree (root->left);
+              printf ("%d\n", root->content_left);
+              output_tree (root->middle);
+              if (root->content_right >= 0)
+                printf ("%d\n", root->content_right);
+              output_tree (root->right);
+            }
+        }
+      \end{lstlisting}
+      Die Datei \gitfile{hp}{20190121}{loesung-3c.c} erweitert dieses Fragment
+      zu einem vollständigen C-Programm zum Erstellen und sortierten Ausgeben
+      eines ternären Baums mit den Zahlenwerten von Aufgabenteil (a).
+  \end{enumerate}
+
+\end{document}
diff --git a/20190121/hp-uebung-20190121.pdf b/20190121/hp-uebung-20190121.pdf
index 849e6d1c2736862258d0b4799e6419c6ce8726c3..aeaa67f68317c7df03a89caf1af5a411374ab342 100644
Binary files a/20190121/hp-uebung-20190121.pdf and b/20190121/hp-uebung-20190121.pdf differ
diff --git a/20190121/hp-uebung-20190121.tex b/20190121/hp-uebung-20190121.tex
index d0a8b6280b6ec6eb7868b91fe17d411d0252b22d..a658160067ab37375a157a1f4b42bd2294c4520b 100644
--- a/20190121/hp-uebung-20190121.tex
+++ b/20190121/hp-uebung-20190121.tex
@@ -20,7 +20,7 @@
 % Attribution-ShareAlike 3.0 Unported License along with this
 % document.  If not, see <http://creativecommons.org/licenses/>.
 
-% README: Stack-Operationen, doppelt verkettete Liste, ternärer Baum
+% README: Stack-Operationen, einfach und doppelt verkettete Listen, ternärer Baum
 
 \documentclass[a4paper]{article}
 
@@ -35,13 +35,13 @@
             Übungsaufgaben -- 21.\ Januar 2019}
 
   Diese Übung enthält Punkteangaben wie in einer Klausur.
-  Um zu "`bestehen"', müssen Sie innerhalb von 90 Minuten
+  Um zu "`bestehen"', müssen Sie innerhalb von 100 Minuten
   unter Verwendung ausschließlich zugelassener Hilfsmittel
-  16 Punkte (von insgesamt \totalpoints) erreichen.
+  17 Punkte (von insgesamt \totalpoints) erreichen.
 
   \exercise{Stack-Operationen}
 
-  Wir betrachten das folgende Programm (\gitfile{hp}{20180122}{aufgabe-1.c}):
+  Wir betrachten das folgende Programm (\gitfile{hp}{20190121}{aufgabe-1.c}):
 
   \begin{minipage}[t]{0.5\textwidth}
     \begin{lstlisting}[gobble=6]
@@ -136,19 +136,19 @@
 
   \goodbreak
 
-  \exercise{Doppelt verkettete Liste}
+  \exercise{Einfach und doppelt verkettete Listen}
 
-  In der Vorlesung wurde ein Beispiel-Programm
-%  (\gitfile{hp}{20180122}{lists-11.c})
-  zur Verwaltung einfach verketteter Listen erstellt
-  (an die Liste anhängen, in die Liste einfügen, Liste ausgeben usw.).
+  Das Beispiel-Programm \gitfile{hp}{20190121}{aufgabe-2.c}
+  demonstriert zwei Funktionen zur Verwaltung einfach verketteter Listen:
+  \lstinline{output_list()} zum Ausgeben der Liste auf den Bildschirm und
+  \lstinline{insert_into_list()} zum Einfügen in die Liste.
 
   \begin{enumerate}[\quad(a)]
     \item
       Ergänzen Sie eine Funktion \lstinline{delete_from_list()}
       zum Löschen eines Elements aus der Liste
       mit Freigabe des Speicherplatzes.
-      \points{3}
+      \points{5}
     \item
       Ergänzen Sie eine Funktion \lstinline{reverse_list()}
       die die Reihenfolge der Elemente in der Liste umdreht.\\
@@ -260,7 +260,7 @@
       wie die Zahlen 7, 137, 3, 5, 6, 42, 1, 2 und 12
       nacheinander und in dieser Reihenfolge
       in den oben beschriebenen Baum eingefügt werden
-      -- analog zu den Vortragsfolien (\gitfile{hp}{20190121}{hp-20980121.pdf}),
+      -- analog zu den Vortragsfolien (\gitfile{hp}{20190121}{hp-20190121.pdf}),
       Seite 43.
       \points{3}
       %
diff --git a/20190121/loesung-1.c b/20190121/loesung-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b87d6c2f977af2843bcc2b75896fb5f00d8fc35
--- /dev/null
+++ b/20190121/loesung-1.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+
+#define STACK_SIZE 10
+
+int stack[STACK_SIZE];
+int stack_pointer = 0;
+
+void push (int x)
+{
+  stack[stack_pointer++] = x;
+}
+
+int pop (void)
+{
+  return stack[--stack_pointer];
+}
+
+void show (void)
+{
+  printf ("stack content:");
+  for (int i = 0; i < stack_pointer; i++)
+    printf (" %d", stack[i]);
+  if (stack_pointer)
+    printf ("\n");
+  else
+    printf (" (empty)\n");
+}
+
+void insert (int x, int pos)
+{
+  for (int i = stack_pointer - 1; i >= pos; i--)
+    stack[i + 1] = stack[i];
+  stack[pos] = x;
+  stack_pointer++;
+}
+
+void insert_sorted (int x)
+{
+  int i = stack_pointer - 1;
+  while (i >= 0 && x < stack[i])
+    {
+      stack[i + 1] = stack[i];
+      i--;
+    }
+  stack[i + 1] = x;
+  stack_pointer++;
+}
+
+int main (void)
+{
+  push (3);
+  push (7);
+  push (137);
+  show ();
+  insert (5, 1);
+  show ();
+  insert_sorted (42);
+  show ();
+  insert_sorted (2);
+  show ();
+  return 0;
+}
diff --git a/20190121/loesung-1b-1.c b/20190121/loesung-1b-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbbe37055109d203105a7ad4acb63d5bef61f4c0
--- /dev/null
+++ b/20190121/loesung-1b-1.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+
+#define STACK_SIZE 10
+
+int stack[STACK_SIZE];
+int stack_pointer = 0;
+
+void push (int x)
+{
+  stack[stack_pointer++] = x;
+}
+
+int pop (void)
+{
+  return stack[--stack_pointer];
+}
+
+void show (void)
+{
+  printf ("stack content:");
+  for (int i = 0; i < stack_pointer; i++)
+    printf (" %d", stack[i]);
+  if (stack_pointer)
+    printf ("\n");
+  else
+    printf (" (empty)\n");
+}
+
+void insert (int x, int pos)
+{
+  for (int i = stack_pointer - 1; i >= pos; i--)
+    stack[i + 1] = stack[i];
+  stack[pos] = x;
+  stack_pointer++;
+}
+
+void insert_sorted (int x)
+{
+  int i = 0;
+  while (i < stack_pointer && x > stack[i])
+    i++;
+  insert (x, i);
+}
+
+int main (void)
+{
+  push (3);
+  push (7);
+  push (137);
+  show ();
+  insert (5, 1);
+  show ();
+  insert_sorted (42);
+  show ();
+  insert_sorted (2);
+  show ();
+  return 0;
+}
diff --git a/20190121/loesung-1b-2.c b/20190121/loesung-1b-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..b1e1ae846f15a29e9cd53a8d1d689e3ccbefacce
--- /dev/null
+++ b/20190121/loesung-1b-2.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+
+#define STACK_SIZE 10
+
+int stack[STACK_SIZE];
+int stack_pointer = 0;
+
+void push (int x)
+{
+  stack[stack_pointer++] = x;
+}
+
+int pop (void)
+{
+  return stack[--stack_pointer];
+}
+
+void show (void)
+{
+  printf ("stack content:");
+  for (int i = 0; i < stack_pointer; i++)
+    printf (" %d", stack[i]);
+  if (stack_pointer)
+    printf ("\n");
+  else
+    printf (" (empty)\n");
+}
+
+void insert (int x, int pos)
+{
+  for (int i = stack_pointer - 1; i >= pos; i--)
+    stack[i + 1] = stack[i];
+  stack[pos] = x;
+  stack_pointer++;
+}
+
+void insert_sorted (int x)
+{
+  int i = stack_pointer - 1;
+  while (i >= 0 && x < stack[i])
+    i--;
+  insert (x, i + 1);
+}
+
+int main (void)
+{
+  push (3);
+  push (7);
+  push (137);
+  show ();
+  insert (5, 1);
+  show ();
+  insert_sorted (42);
+  show ();
+  insert_sorted (2);
+  show ();
+  return 0;
+}
diff --git a/20190121/loesung-1c.c b/20190121/loesung-1c.c
new file mode 100644
index 0000000000000000000000000000000000000000..79d061e3d7b78a2bec05b632e74b083a16d5a326
--- /dev/null
+++ b/20190121/loesung-1c.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+
+#define STACK_SIZE 10
+
+int stack[STACK_SIZE];
+int stack_pointer = 0;
+
+void push (int x)
+{
+  stack[stack_pointer++] = x;
+}
+
+int pop (void)
+{
+  return stack[--stack_pointer];
+}
+
+void show (void)
+{
+  printf ("stack content:");
+  for (int i = 0; i < stack_pointer; i++)
+    printf (" %d", stack[i]);
+  if (stack_pointer)
+    printf ("\n");
+  else
+    printf (" (empty)\n");
+}
+
+void insert (int x, int pos)
+{
+  for (int i = stack_pointer - 1; i >= pos; i--)
+    stack[i + 1] = stack[i];
+  stack[pos] = x;
+  stack_pointer++;
+}
+
+void insert_sorted (int x)
+{
+  int i = stack_pointer - 1;
+  while (i >= 0 && x < stack[i])
+    {
+      stack[i + 1] = stack[i];
+      i--;
+    }
+  stack[i + 1] = x;
+  stack_pointer++;
+}
+
+int search (int x)
+{
+  for (int i = 0; i < stack_pointer; i++)
+    if (stack[i] == x)
+      return i;
+  return -1;
+}
+
+int main (void)
+{
+  push (3);
+  push (7);
+  push (137);
+  show ();
+  insert (5, 1);
+  show ();
+  insert_sorted (42);
+  show ();
+  insert_sorted (2);
+  show ();
+  printf ("%d\n", search (42));
+  printf ("%d\n", search (1117));
+  return 0;
+}
diff --git a/20190121/loesung-1d.c b/20190121/loesung-1d.c
new file mode 100644
index 0000000000000000000000000000000000000000..9079d8d3be7a500498aede2cf8e89a7d7db48a35
--- /dev/null
+++ b/20190121/loesung-1d.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+
+#define STACK_SIZE 10
+
+int stack[STACK_SIZE];
+int stack_pointer = 0;
+
+void push (int x)
+{
+  stack[stack_pointer++] = x;
+}
+
+int pop (void)
+{
+  return stack[--stack_pointer];
+}
+
+void show (void)
+{
+  printf ("stack content:");
+  for (int i = 0; i < stack_pointer; i++)
+    printf (" %d", stack[i]);
+  if (stack_pointer)
+    printf ("\n");
+  else
+    printf (" (empty)\n");
+}
+
+void insert (int x, int pos)
+{
+  for (int i = stack_pointer - 1; i >= pos; i--)
+    stack[i + 1] = stack[i];
+  stack[pos] = x;
+  stack_pointer++;
+}
+
+void insert_sorted (int x)
+{
+  int i = stack_pointer - 1;
+  while (i >= 0 && x < stack[i])
+    {
+      stack[i + 1] = stack[i];
+      i--;
+    }
+  stack[i + 1] = x;
+  stack_pointer++;
+}
+
+int search (int x)
+{
+  int left = 0;
+  int right = stack_pointer;
+  while (left < right - 1)
+    {
+      int middle = (left + right) / 2;
+      if (x < stack[middle])
+        right = middle;
+      else
+        left = middle;
+    }
+  if (x == stack[left])
+    return left;
+  else
+   return -1;
+}
+
+int main (void)
+{
+  push (3);
+  push (7);
+  push (137);
+  show ();
+  insert (5, 1);
+  show ();
+  insert_sorted (42);
+  show ();
+  insert_sorted (2);
+  show ();
+  printf ("%d\n", search (2));
+  printf ("%d\n", search (4));
+  printf ("%d\n", search (42));
+  printf ("%d\n", search (67));
+  printf ("%d\n", search (137));
+  printf ("%d\n", search (1117));
+  return 0;
+}
diff --git a/20190121/loesung-2a.c b/20190121/loesung-2a.c
new file mode 100644
index 0000000000000000000000000000000000000000..93f5e5629a98d13d6292c56d1a3f855b4f5bd8fd
--- /dev/null
+++ b/20190121/loesung-2a.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct node
+{
+  int content;
+  struct node *next;
+} node;
+
+void output_list (node *first)
+{
+  for (node *p = first; p; p = p->next)
+    printf ("%d ", p->content);
+  printf ("\n");
+}
+
+void insert_into_list (node *what, node *where)
+{
+  what->next = where->next;
+  where->next = what;
+}
+
+void delete_from_list (node *what, node **first)
+{
+  if (what == *first)
+    *first = what->next;
+  else
+    {
+      node *p = *first;
+      while (p && p->next != what)
+        p = p->next;
+      if (p)
+        p->next = what->next;
+    }
+  free (what);
+}
+
+int main (void)
+{
+  node *element3 = malloc (sizeof (node));
+  node *element7 = malloc (sizeof (node));
+  node *element137 = malloc (sizeof (node));
+
+  element3->content = 3;
+  element7->content = 7;
+  element137->content = 137;
+
+  node *first = element3;
+  element3->next = element7;
+  element7->next = element137;
+  element137->next = NULL;
+
+  output_list (first);
+
+  node *element5 = malloc (sizeof (node));
+  element5->content = 5;
+  insert_into_list (element5, element3);
+
+  output_list (first);
+
+  delete_from_list (element5, &first);
+  output_list (first);
+  delete_from_list (element3, &first);
+  output_list (first);
+  delete_from_list (element137, &first);
+  output_list (first);
+
+  return 0;
+}
diff --git a/20190121/loesung-2b.c b/20190121/loesung-2b.c
new file mode 100644
index 0000000000000000000000000000000000000000..811b99b1fc9697ca0302eea8950c9bc6e7ab6042
--- /dev/null
+++ b/20190121/loesung-2b.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct node
+{
+  int content;
+  struct node *next, *prev;
+} node;
+
+void check_list (node *first)
+{
+  for (node *p = first; p; p = p->next)
+    {
+      if (p->next && p->next->prev != p)
+        fprintf (stderr, "List inconsistency!\n");
+      if (p->prev && p->prev->next != p)
+        fprintf (stderr, "List inconsistency!\n");
+    }
+}
+
+void output_list (node *first)
+{
+  for (node *p = first; p; p = p->next)
+    printf ("%d ", p->content);
+  printf ("\n");
+}
+
+void insert_into_list (node *what, node *where)
+{
+  what->next = where->next;
+  if (where->next)
+    where->next->prev = what;
+  what->prev = where;
+  where->next = what;
+}
+
+void delete_from_list (node *what, node **first)
+{
+  if (what == *first)
+    {
+      *first = what->next;
+      if (*first)
+        (*first)->prev = NULL;
+    }
+  else
+    {
+      node *p = *first;
+      while (p && p->next != what)
+        p = p->next;
+      if (p)
+        p->next = what->next;
+      if (what->next)
+        what->next->prev = p;
+    }
+  free (what);
+}
+
+int main (void)
+{
+  node *element3 = malloc (sizeof (node));
+  node *element7 = malloc (sizeof (node));
+  node *element137 = malloc (sizeof (node));
+
+  element3->content = 3;
+  element7->content = 7;
+  element137->content = 137;
+
+  node *first = element3;
+  element3->prev = NULL;
+  element3->next = element7;
+  element7->prev = element3;
+  element7->next = element137;
+  element137->prev = element7;
+  element137->next = NULL;
+
+  output_list (first);
+  check_list (first);
+
+  node *element5 = malloc (sizeof (node));
+  element5->content = 5;
+  insert_into_list (element5, element3);
+
+  output_list (first);
+  check_list (first);
+
+  delete_from_list (element5, &first);
+  output_list (first);
+  check_list (first);
+  delete_from_list (element3, &first);
+  output_list (first);
+  check_list (first);
+  delete_from_list (element137, &first);
+  output_list (first);
+  check_list (first);
+
+  return 0;
+}
diff --git a/20190121/loesung-3c.c b/20190121/loesung-3c.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6dea990b25895475e343f07895c6a946426d2d8
--- /dev/null
+++ b/20190121/loesung-3c.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct node
+{
+  int content_left, content_right;
+  struct node *left, *middle, *right;
+} node;
+
+void insert_into_tree (node **root, int value)
+{
+  if (*root)
+    {
+      if ((*root)->content_right >= 0)
+        if (value < (*root)->content_left)
+          insert_into_tree (&(*root)->left, value);
+        else if (value < (*root)->content_right)
+          insert_into_tree (&(*root)->middle, value);
+        else
+          insert_into_tree (&(*root)->right, value);
+      else
+        if (value < (*root)->content_left)
+          {
+            (*root)->content_right = (*root)->content_left;
+            (*root)->content_left = value;
+          }
+        else
+          (*root)->content_right = value;
+    }
+  else
+    {
+      *root = malloc (sizeof (node));
+      (*root)->left = NULL;
+      (*root)->content_left = value;
+      (*root)->middle = NULL;
+      (*root)->content_right = -1;
+      (*root)->right = NULL;
+    }
+}
+
+void output_tree (node *root)
+{
+  if (root)
+    {
+      output_tree (root->left);
+      printf ("%d\n", root->content_left);
+      output_tree (root->middle);
+      if (root->content_right >= 0)
+        printf ("%d\n", root->content_right);
+      output_tree (root->right);
+    }
+}
+
+int main (void)
+{
+  node *root = NULL;
+  insert_into_tree (&root, 7);
+  insert_into_tree (&root, 137);
+  insert_into_tree (&root, 3);
+  insert_into_tree (&root, 5);
+  insert_into_tree (&root, 6);
+  insert_into_tree (&root, 42);
+  insert_into_tree (&root, 1);
+  insert_into_tree (&root, 2);
+  insert_into_tree (&root, 12);
+  output_tree (root);
+  return 0;
+}