Nächste Seite: Über dieses Dokument ...
Aufwärts: Bäume
Vorherige Seite: Operationen auf AVL-Bäumen
  Inhalt
Die folgende Beispielimplementierung hält sich nicht immer an die
verwendeten Bezeichnungen in diesem Dokument. Sie ist aber voll
funktionsfähig und soll der veranschaulichung dienen.
(****************************************
* Description:
* Implementiert einen AVL-Baum
* in Oberon.
* Author:
* Daniel Hottinger <hodaniel@iiic.ethz.ch>
* Department of Computer Science, ETH Zurich
* SS 2001
* Licence: GNU GPL v2 or later
* Created: Mon May 14 20:16:37 CEST 2001
* Last update: none
* Changes:
****************************************)
MODULE AVLTree;
IMPORT
Out;
CONST
LESS = -1;
EQUAL = 0;
MORE = 1;
TYPE
PKey* = POINTER TO TKey;
TKey* = RECORD
END;
PValue* = POINTER TO TValue;
TValue* = RECORD
END;
TCompareFunc* = PROCEDURE (key1, key2: PKey): LONGINT;
TPrintProc = PROCEDURE(key: PKey);
PAVLNode = POINTER TO TAVLNode;
TAVLNode = RECORD
balance: LONGINT;
left, right: PAVLNode;
key: PKey;
value: PValue;
END;
PAVLTree* = POINTER TO TAVLTree;
TAVLTree* = RECORD
root: PAVLNode;
cmp: TCompareFunc;
END;
(****************************************
* Speicherverwaltung
****************************************)
PROCEDURE NewNode(key: PKey; value: PValue): PAVLNode;
VAR
node: PAVLNode;
BEGIN
NEW(node);
node^.balance := 0;
node^.left := NIL;
node^.right := NIL;
node^.key := key;
node^.value := value;
RETURN node;
END NewNode;
(* One day we'll reuse these nodes, *)
(* so oberon can do less wrong *)
PROCEDURE DestroyNode(VAR node: PAVLNode);
BEGIN
IF node # NIL THEN
DestroyNode(node^.left);
DestroyNode(node^.right);
END;
(* Hope the oberon garbage collector works *)
node := NIL;
END DestroyNode;
PROCEDURE NewTree*(cmp: TCompareFunc): PAVLTree;
VAR
tree: PAVLTree;
BEGIN
NEW(tree);
tree^.root := NIL;
tree^.cmp := cmp;
RETURN tree;
END NewTree;
PROCEDURE DestroyTree*(VAR tree: PAVLTree);
BEGIN
IF tree # NIL THEN
DestroyNode(tree^.root);
tree := NIL;
END;
END DestroyTree;
(****************************************
* Rotationen
****************************************)
(****************************************
* n(l,r(rl,rr)) =>
* r(n(l,rl),rr)
*
* n r
* / `_ _.' \
* l r => n rr
* / \ / \
* rl rr l rl
****************************************)
PROCEDURE RotLeft(node: PAVLNode): PAVLNode;
VAR
left, right: PAVLNode;
abal, bbal: LONGINT;
BEGIN
left := node^.left;
right := node^.right;
node^.right := right^.left;
right^.left := node;
abal := node^.balance;
bbal := right^.balance;
IF bbal <= EQUAL THEN
IF abal >= MORE THEN
right^.balance := bbal - 1;
ELSE
right^.balance := abal + bbal - 2;
END;
node^.balance := abal - 1;
ELSE
IF abal <= bbal THEN
right^.balance := abal - 2;
ELSE
right^.balance := bbal - 1;
END;
node^.balance := abal - bbal - 1;
END;
RETURN right;
END RotLeft;
(****************************************
* n(l(ll,lr),r) =>
* l(ll,n(lr,r))
*
* n l
* _.' \ / `_
* l r => ll n
* / \ / \
* ll lr lr r
****************************************)
PROCEDURE RotRight(node: PAVLNode): PAVLNode;
VAR
left, right: PAVLNode;
abal, bbal: LONGINT;
BEGIN
left := node^.left;
right := node^.right;
node^.left := left^.right;
left^.right := node;
abal := node^.balance;
bbal := left^.balance;
IF bbal <= EQUAL THEN
IF abal < bbal THEN
left^.balance := bbal + 1;
ELSE
left^.balance := abal + 2;
END;
node^.balance := abal - bbal + 1;
ELSE
IF abal <= LESS THEN
left^.balance := bbal + 1;
ELSE
left^.balance := abal + bbal + 2;
END;
node^.balance := abal + 1;
END;
RETURN left;
END RotRight;
(****************************************
* Ausbalancieren
****************************************)
(****************************************
* Beispiel: (Doppelrotation)
* 2(1,6(5(4),7)) =>
* 2(1,5(4,6(,7))) =>
* 5(2(1,4),6(,7))
*
* 2 2
* / `-._ / `_ 5
* 1 6 1 5 / \
* / \ => / \ => 2 6
* 5 7 4 6 ^ \
* / \ 1 4 7
* 4 7
****************************************)
PROCEDURE BalanceNode(node: PAVLNode): PAVLNode;
BEGIN
IF node^.balance < LESS THEN
IF node^.left^.balance > EQUAL THEN
node^.left := RotLeft(node^.left);
END;
node := RotRight(node);
ELSIF node^.balance > MORE THEN
IF node^.right^.balance < EQUAL THEN
node^.right := RotRight(node^.right); (* siehe Beispiel *)
END;
node := RotLeft(node);
END;
RETURN node;
END BalanceNode;
(* Called after a node of node^.left was removed *)
PROCEDURE RestoreLeftBalance(node: PAVLNode; oldbalance: LONGINT): PAVLNode;
BEGIN
IF node^.left = NIL THEN
INC(node^.balance);
ELSIF (node^.left^.balance # oldbalance) & (node^.left^.balance = 0) THEN
(* left tree shrunk *)
INC(node^.balance);
END;
IF node^.balance > MORE THEN
RETURN BalanceNode(node);
ELSE
RETURN node;
END;
END RestoreLeftBalance;
(* Called after a node of node^.right was removed *)
PROCEDURE RestoreRightBalance(node: PAVLNode; oldbalance: LONGINT): PAVLNode;
BEGIN
IF node^.right = NIL THEN
DEC(node^.balance);
ELSIF (node^.right^.balance # oldbalance) & (node^.right^.balance = 0) THEN
(* right tree shrunk *)
DEC(node^.balance);
END;
IF node^.balance > LESS THEN
RETURN BalanceNode(node);
ELSE
RETURN node;
END;
END RestoreRightBalance;
PROCEDURE RemoveNodeMostLeft(node: PAVLNode; VAR leftmost: PAVLNode): PAVLNode;
VAR
oldbalance: LONGINT;
BEGIN
IF node^.left = NIL THEN
leftmost := node;
RETURN node^.right;
END;
oldbalance := node^.left^.balance;
node^.left := RemoveNodeMostLeft(node^.left, leftmost);
RETURN RestoreLeftBalance(node, oldbalance);
END RemoveNodeMostLeft;
(****************************************
* grundlegende Operationen
****************************************)
PROCEDURE InsertNode(node: PAVLNode; cmp: TCompareFunc; key: PKey;
value: PValue; VAR inserted: BOOLEAN): PAVLNode;
VAR
relation: LONGINT;
oldbalance: LONGINT;
BEGIN
IF node = NIL THEN
inserted := TRUE;
RETURN NewNode(key, value);
END;
relation := cmp(key, node^.key);
IF relation = EQUAL THEN
(* Don't insert dublicate key/value *)
inserted := FALSE;
RETURN node;
ELSIF relation = LESS THEN
IF node^.left # NIL THEN
oldbalance := node^.left^.balance;
node^.left := InsertNode(node^.left, cmp, key, value, inserted);
IF (oldbalance # node^.left^.balance) & (node^.left^.balance # 0) THEN
(* Tree has grown *)
DEC(node^.balance);
END;
ELSE
inserted := TRUE;
node^.left := NewNode(key, value);
DEC(node^.balance);
END;
ELSIF relation = MORE THEN
IF node^.right # NIL THEN
oldbalance := node^.right^.balance;
node^.right := InsertNode(node^.right, cmp, key, value, inserted);
IF (oldbalance # node^.right^.balance) & (node^.right^.balance # 0) THEN
(* Tree has grown *)
INC(node^.balance);
END;
ELSE
inserted := TRUE;
node^.right := NewNode(key, value);
INC(node^.balance);
END;
END;
IF inserted THEN
IF ABS(node^.balance) > 1 THEN
node := BalanceNode(node);
END;
END;
RETURN node;
END InsertNode;
PROCEDURE Insert*(tree: PAVLTree; key: PKey; value: PValue);
VAR
inserted: BOOLEAN;
BEGIN
IF tree # NIL THEN
inserted := FALSE;
tree^.root := InsertNode(tree^.root, tree^.cmp, key, value, inserted);
END;
END Insert;
(****************************************
* Beispiel:
* n(l(:,:),r(rl(rll,rlr),rr)) =>
* n(l(:,:),r(rl(,rlr),rr)) =>
* rll(l(:,:),r(rl(,rlr),rr))
* n n rll
* / `--...___ / `-..__ / `-..__
* l r l r l r
* ^ __.-' \ => ^ __.-' \ => ^ __.-' \
* : : rl rr : : rl rr : : rl rr
* _' \ \ \
* rll rlr rlr rlr
****************************************)
PROCEDURE RemoveNode(node: PAVLNode; cmp: TCompareFunc; key: PKey): PAVLNode;
VAR
relation, oldbalance: LONGINT;
garbage, newroot: PAVLNode;
BEGIN
IF node = NIL THEN
RETURN NIL;
END;
relation := cmp(key, node^.key);
IF relation = EQUAL THEN
garbage := node;
IF node^.right = NIL THEN
node := node^.left;
ELSE
oldbalance := node^.right^.balance;
(* new right node is the leftmost of the right tree *)
(* Beispiel *)
node^.right := RemoveNodeMostLeft(node^.right, newroot);
newroot^.left := node^.left;
newroot^.right := node^.right;
newroot^.balance := node^.balance;
node := RestoreRightBalance(newroot, oldbalance);
END;
(* free *only* the removed node *)
garbage^.right := NIL;
garbage^.left := NIL;
DestroyNode(garbage);
ELSIF relation = LESS THEN
IF node^.left # NIL THEN
oldbalance := node^.left^.balance;
node^.left := RemoveNode(node^.left, cmp, key);
node := RestoreLeftBalance(node, oldbalance);
END;
ELSIF relation = MORE THEN
IF node^.right # NIL THEN
oldbalance := node^.right^.balance;
node^.right := RemoveNode(node^.right, cmp, key);
node := RestoreRightBalance(node, oldbalance);
END;
END;
RETURN node;
END RemoveNode;
PROCEDURE Remove*(tree: PAVLTree; key: PKey);
BEGIN
IF tree # NIL THEN
tree^.root := RemoveNode(tree^.root, tree^.cmp, key);
END;
END Remove;
(****************************************
* Debug
****************************************)
PROCEDURE Traverse(node: PAVLNode; print: TPrintProc);
BEGIN
IF node # NIL THEN
print(node^.key);
IF (node^.left # NIL) OR (node^.right # NIL) THEN
Out.String("(");
Traverse(node^.left, print);
IF node^.right # NIL THEN
Out.String(",");
END;
Traverse(node^.right, print);
Out.String(")");
END;
END;
END Traverse;
PROCEDURE Dump*(tree: PAVLTree; print: TPrintProc);
BEGIN
Traverse(tree^.root, print);
END Dump;
END AVLTree.
Daniel Hottinger
2001-05-16