Treaps / Baufen / Balden in Haskell
05.01.2011 von Niels Hoppe
Die Datenstruktur des Binären Suchbaumes ist wohl den Meisten bekannt
und die des Heaps auch. Die Kombination aus beiden ergibt den Treap
(aus Tree und Heap) bzw. Baufen (aus Baum und Haufen), oder auch die
Balde (aus Baum und Halde). Jeder Knoten besteht hat dabei einen
Schlüssel und eine Priorität. Diese Datenstruktur eignet sich besonders
zur Implementierung von Prioritätswarteschlangen.
In einer Übung in
Algorithmen und Programmierung III sollten nun Funktionen zum Einfügen
in einen und Löschen aus einem Treap entwickelt werden.
Dazu schreiben wir drei Funktionen in Haskell: Zum Einfügen insert, zum Löschen delete und als Hilfsfunktion rotate. Sie arbeiten jeweils rekursiv auf der Daten-
struktur des Treaps, die wir wie folgt definieren:
data (Ord a, Real b) => Treap a b
= Nil
| Node (a, b) (Treap a b) (Treap a b)
insert fügt ein neues
Tupel aus Schlüssel und Priorität in einen Treap ein. Dazu wird es
zuerst als neues Blatt wie in einem Binären Suchbaum eingefügt, dann
wird der entsprechende Ast rekursiv vom neuen Blatt in Richtung Wurzel
so rotiert, dass die Heap-Eigenschaft wiederhergestellt wird.
insert :: (Ord a, Real b) => (a, b) -> Treap a b -> Treap a b
-- als neues Blatt einfügen
insert tup Nil = Node tup Nil Nil
-- in Richtung der Blätter durch reichen und durch Rotation Struktur reparieren
insert (k, p) (Node (l, q) lft rgt)
| k < l = rotate (Node (l, q) (insert (k, p) lft) rgt)
| k > l = rotate (Node (l, q) lft (insert (k, p) rgt))
| k == l = Node (k, p) lft rgt
delete entfernt ein Tupel mit
einem gegebenen Schlüssel aus einem Treap. Dazu wird es gesucht, seine
Priorität nach und nach vergrößert, so dass es an das Ende des Treaps
sinkt und es dann als Blatt gelöscht.
delete :: (Ord a, Real b) => a -> Treap a b -> Treap a b
-- Löschen aus einem Blatt
delete k leaf@(Node (l, q) Nil Nil)
| k == l = Nil
| otherwise = leaf
-- Ist nur ein linkes Kind vorhanden, wird links weiter gesucht, falls der zu
-- löschende Schlüssel kleiner als der des Knotens ist, sonst wird die Suche
-- abgebrochen. Wurde der Schlüssel gefunden, wird er gelöscht.
delete k node@(Node (l, q)
lft@(Node (m, r) ll lr)
Nil
)
| k < l = (Node (l, q) (delete k lft) Nil)
| k > l = node
| otherwise = rotate (delete k (rotate (Node (l, (r + 1)) lft Nil)))
-- Ist nur ein rechtes Kind vorhanden, wird rechts weiter gesucht, falls der zu
-- löschende Schlüssel größer als der des Knotens ist, sonst wird die Suche
-- abgebrochen. Wurde der Schlüssel gefunden, wird er gelöscht.
delete k node@(Node (l, q)
Nil
rgt@(Node (n, s) rl rr)
)
| k < l = node
| k > l = (Node (l, q) Nil (delete k rgt))
| otherwise = rotate (delete k (rotate (Node (l, (s + 1)) Nil rgt)))
-- Sind ein linkes und ein rechtes Kind vorhanden, wird erst links, dann rechts
-- weiter gesucht, falls der zu löschende Schlüssel kleiner bzw. größer als der
-- des Knotens ist. Wurde der Schlüssel gefunden, wird er gelöscht.
delete k (Node (l, q)
lft@(Node (m, r) ll lr)
rgt@(Node (n, s) rl rr)
)
| k < l = (Node (l, q) (delete k lft) rgt)
| k > l = (Node (l, q) lft (delete k rgt))
| otherwise = rotate (delete k (rotate (Node (l, (max r s + 1)) lft rgt)))
rotate rotiert um einen Knoten, um
die Heap-Eigenschaft wiederherzustellen. Es wird vorausgesetzt, dass
sie nur in einem Teilbaum gestört ist.
rotate :: (Ord a, Real b) => Treap a b -> Treap a b
-- An einem Blatt verändert sich Nichts
rotate (Node tup Nil Nil) = Node tup Nil Nil
-- Ist nur ein linkes Kind vorhanden, wird die Heap-Eigenschaft geprüft und wenn
-- nötig nach rechts rotiert.
rotate (Node (k, p) lft@(Node (l, q) ll lr) Nil)
| p > q = Node (l, q) ll (Node (k, p) lr Nil)
| otherwise = Node (k, p) lft Nil
-- Ist nur ein rechtes Kind vorhanden, wird die Heap-Eigenschaft geprüft und
-- wenn nötig nach links rotiert.
rotate (Node (k, p) Nil rgt@(Node (m, r) rl rr))
| p > r = Node (m, r) (Node (k, p) Nil rl) rr
| otherwise = Node (k, p) Nil rgt
-- Sind ein linkes und ein rechtes Kind vorhanden, wird erst links, dann rechts
-- die Heap-Eigenschaft geprüft und wenn nötig in die entsprechende Richtung
-- rotiert. Die Heap-Eigenschaft darf nur auf einer Seite nicht gegeben sein!
rotate (Node (k, p)
lft@(Node (l, q) ll lr)
rgt@(Node (m, r) rl rr)
)
| p > q = Node (l, q) ll (Node (k, p) lr rgt)
| p > r = Node (m, r) (Node (k, p) lft rl) rr
| otherwise = Node (k, p) lft rgt
