home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / Misc / OB3.2D2.DMS / in.adf / Module / AVL.mod < prev    next >
Encoding:
Text File  |  1994-08-05  |  12.0 KB  |  528 lines

  1. (*-------------------------------------------------------------------------*)
  2. (*                                                                         *)
  3. (*  Amiga Oberon Library Module: AVL                  Date: 02-Nov-92      *)
  4. (*                                                                         *)
  5. (*   © 1992 by Fridtjof Siebert                                            *)
  6. (*                                                                         *)
  7. (*-------------------------------------------------------------------------*)
  8.  
  9. MODULE AVL;
  10.  
  11. IMPORT BT * := BasicTypes;
  12.  
  13. TYPE
  14.  
  15.   NodePtr * = POINTER TO Node;
  16.   Node * = RECORD (BT.ANYDesc)
  17.              l   - : NodePtr;
  18.              r   - : NodePtr;
  19.              bal   : INTEGER;
  20.            END;
  21.  
  22.   RootPtr * = POINTER TO Root;
  23.   Root * = RECORD (BT.COLLECTIONDesc)
  24.              root - : NodePtr;
  25.              cmp    : PROCEDURE(a,b: NodePtr): INTEGER;
  26.              find   : PROCEDURE(VAR root: Root; a: NodePtr): INTEGER;
  27.            END;
  28.  
  29.  
  30.   CompProc * = PROCEDURE(a,b: NodePtr): INTEGER;
  31.  
  32. (* Prozedur zum Vergleichen zweier Nodes.
  33.    Ergebnis ist:
  34.      = 0, wenn beide gleich sind
  35.      < 0, wenn a < b
  36.      > 0, wenn a > b
  37. *)
  38.  
  39.   FindProc * = PROCEDURE(VAR root: Root; a: NodePtr): INTEGER;
  40.  
  41. (* Diese Prozedur wird von Find() verwendet.
  42.    Ihr Ergebnis ist
  43.      = 0, wenn a das gesuchte Element ist
  44.      < 0, wenn a < dem gesuchten Element ist
  45.      > 0, wenn a > dem gesuchten Element ist
  46. *)
  47.  
  48.   DoProc * = PROCEDURE(a: NodePtr);
  49.  
  50. CONST
  51.  
  52. (* Node.bal *)
  53.   left  * = -1;
  54.   ok    * = 0;
  55.   right * = 1;
  56.  
  57.  
  58. TYPE
  59.   String * = ARRAY 80 OF CHAR;
  60.  
  61.   SNodePtr * = POINTER TO SNode;
  62.   SNode * = RECORD (Node)
  63.               name * : String;
  64.             END;
  65.  
  66.   SRoot * = RECORD (Root)
  67.               findstr: String;
  68.             END;
  69.  
  70.  
  71. (*-------------------------------------------------------------------------*)
  72.  
  73.  
  74. (*------  Init:  ------*)
  75.  
  76.  
  77. PROCEDURE Init* (VAR root: Root;
  78.                      cmp:  CompProc;
  79.                      find: FindProc);
  80.  
  81. (* initialisiert (leert) den AVL-Baum und setzt die Vergleichsprozedur und
  82.    Suchprozedur. Diesen Prozeduren werden die Nodes übergeben, die mit
  83.    Add() in den Baum eingetragen wurden. *)
  84.  
  85. BEGIN
  86.   root.root := NIL;
  87.   root.cmp  := cmp;
  88.   root.find := find;
  89. END Init;
  90.  
  91.  
  92. (*------  Add:  ------*)
  93.  
  94.  
  95. PROCEDURE Add* (VAR root: Root;
  96.                     node: NodePtr): BOOLEAN;
  97.  
  98. (* fügt node in den Baum ein. Das Ergebnis ist FALSE, wenn ein gleicher
  99.    (z.B. gleichnamiger) Knoten bereits im Baum enthalten ist, also nicht
  100.    eingefügt werden konnte. *)
  101.  
  102. VAR
  103.   res: BOOLEAN;
  104.   grown: BOOLEAN;
  105.   n1,n2: NodePtr;
  106.  
  107.   PROCEDURE Search(VAR n: NodePtr);
  108.  
  109.   VAR c: INTEGER;
  110.  
  111.   BEGIN
  112.     IF n=NIL THEN                       (* neues Element einfügen: *)
  113.       grown := TRUE;
  114.       n := node;
  115.       n.l := NIL; n.r := NIL; n.bal := 0;
  116.       res := TRUE;
  117.     ELSE
  118.       c := root.cmp(n,node);
  119.       IF c>0 THEN
  120.         Search(n.l);
  121.         IF grown THEN                   (* linker Zweig gewachsen: *)
  122.           CASE n.bal OF
  123.           right: n.bal := ok; grown := FALSE |
  124.           ok:    n.bal := left |
  125.           left:  n1 := n.l;             (* rebalance *)
  126.                  IF n1.bal = left THEN  (* einfache LL Rotation *)
  127.                    n.l := n1.r; n1.r := n; n.bal := ok; n := n1
  128.                  ELSE                   (* doppelte LR Rotation *)
  129.                    n2 := n1.r; n1.r := n2.l; n2.l := n1; n.l := n2.r; n2.r := n;
  130.                    IF n2.bal=left  THEN n .bal := right ELSE n .bal := ok END;
  131.                    IF n2.bal=right THEN n1.bal := left  ELSE n1.bal := ok END;
  132.                    n := n2;
  133.                  END;
  134.                  n.bal := ok; grown := FALSE |
  135.           END;
  136.         END;
  137.       ELSIF c<0 THEN
  138.         Search(n.r);
  139.         IF grown THEN                   (* rechter Zweig gewachsen: *)
  140.           CASE n.bal OF
  141.           left:  n.bal := ok; grown := FALSE |
  142.           ok:    n.bal := right |
  143.           right: n1 := n.r;             (* rebalance *)
  144.                  IF n1.bal = right THEN (* einfache RR Rotation *)
  145.                    n.r := n1.l; n1.l := n; n.bal := ok; n := n1
  146.                  ELSE                   (* doppelte RL Rotation *)
  147.                    n2 := n1.l; n1.l := n2.r; n2.r := n1;
  148.                    n.r  := n2.l; n2.l := n;
  149.                    IF n2.bal = right THEN n .bal := left  ELSE n .bal := ok END;
  150.                    IF n2.bal = left  THEN n1.bal := right ELSE n1.bal := ok END;
  151.                    n := n2;
  152.                  END;
  153.                  n.bal := ok; grown := FALSE |
  154.           END;
  155.         END;
  156.       ELSE
  157.         grown := FALSE;
  158.         res := FALSE;
  159.       END;
  160.     END;
  161.   END Search;
  162.  
  163. BEGIN
  164.   Search(root.root);
  165.   RETURN res;
  166. END Add;
  167.  
  168.  
  169. (*------  Find:  ------*)
  170.  
  171.  
  172. PROCEDURE Find* (VAR root: Root): NodePtr;
  173.  
  174. (* Sucht denjenigen Knoten, für den Root.FindProc das Ergebnis 0 liefert und
  175.    gibt seine Adresse oder NIL zurück. *)
  176.  
  177. VAR
  178.   n: NodePtr;
  179.   c: INTEGER;
  180.  
  181. BEGIN
  182.   n := root.root;
  183.   LOOP
  184.     IF n=NIL THEN RETURN NIL END;
  185.     c := root.find(root,n);
  186.     IF    c<0 THEN n := n.r
  187.     ELSIF c>0 THEN n := n.l
  188.               ELSE RETURN n END;
  189.   END;
  190. END Find;
  191.  
  192.  
  193. (*------  Remove:  ------*)
  194.  
  195.  
  196. PROCEDURE Remove* (VAR root: Root;
  197.                        node: NodePtr): BOOLEAN;
  198.  
  199. (* Entfernt node aus dem Baum. Ergebnis ist FALSE, wenn node nicht im
  200.    Baum enthalten war. Der Speicher von node muß danach noch freigegeben werden. *)
  201.  
  202. VAR
  203.   h: BOOLEAN;
  204.   deleted: BOOLEAN;
  205.  
  206.  
  207.   PROCEDURE BalL(VAR n: NodePtr);
  208.   VAR n1, n2 : NodePtr;
  209.       b1, b2 : INTEGER;
  210.   BEGIN
  211.     CASE n.bal OF
  212.     left:  n.bal := ok |
  213.     ok:    n.bal := right; h := FALSE |
  214.     right: n1 := n.r; b1 := n1.bal;     (* rebalance *)
  215.            IF b1 # left THEN            (* single RR rotation *)
  216.              n.r := n1.l; n1.l := n;
  217.              IF b1 = ok THEN n.bal := right; n1.bal := left; h := FALSE
  218.                         ELSE n.bal := ok;    n1.bal := ok;               END;
  219.              n := n1;
  220.            ELSE                         (* double RL rotation *)
  221.              n2 := n1.l; b2 := n2.bal;
  222.              n1.l := n2.r; n2.r := n1;
  223.              n .r := n2.l; n2.l := n;
  224.              IF b2 = right THEN n .bal := left  ELSE n .bal := ok END;
  225.              IF b2 = left  THEN n1.bal := right ELSE n1.bal := ok END;
  226.              n := n2; n2.bal := ok;
  227.            END |
  228.     END;
  229.   END BalL;
  230.  
  231.   PROCEDURE BalR(VAR n: NodePtr);
  232.   VAR n1, n2 : NodePtr;
  233.       b1, b2 : INTEGER;
  234.   BEGIN
  235.     CASE n.bal OF
  236.     right: n.bal :=  ok |
  237.     ok:    n.bal := left; h := FALSE |
  238.     left:  n1 := n.l; b1 := n1.bal;     (* rebalance *)
  239.            IF b1 # right THEN           (* single LL rotation *)
  240.              n.l := n1.r; n1.r := n;
  241.              IF b1 = ok THEN n.bal := left; n1.bal := right; h := FALSE
  242.                         ELSE n.bal := ok;   n1.bal := ok;               END;
  243.              n := n1;
  244.            ELSE                         (* double LR rotation *)
  245.              n2 := n1.r; b2 := n2.bal;
  246.              n1.r := n2.l; n2.l := n1;
  247.              n .l := n2.r; n2.r := n;
  248.              IF b2 = left  THEN n .bal := right ELSE n .bal := ok END;
  249.              IF b2 = right THEN n1.bal := left  ELSE n1.bal := ok END;
  250.              n := n2; n2.bal := ok;
  251.            END |
  252.     END;
  253.   END BalR;
  254.  
  255.   PROCEDURE Rem(VAR n: NodePtr);
  256.  
  257.   VAR
  258.     c: INTEGER;
  259.     New,Q: NodePtr;
  260.  
  261.     PROCEDURE del(VAR m: NodePtr);
  262.     VAR
  263.       s: Node;
  264.     BEGIN
  265.       IF m.r#NIL THEN
  266.         del(m.r); IF h THEN BalR(m) END
  267.       ELSE
  268.         New := m;
  269.         m := m.l;
  270.         h := TRUE;
  271.       END;
  272.     END del;
  273.  
  274.   BEGIN
  275.     IF n # NIL THEN
  276.       c := root.cmp(n,node);
  277.       IF    c>0 THEN Rem(n.l); IF h THEN BalL(n) END
  278.       ELSIF c<0 THEN Rem(n.r); IF h THEN BalR(n) END
  279.       ELSE
  280.         deleted := TRUE;
  281.         IF    n.r=NIL THEN n := n.l; h := TRUE
  282.         ELSIF n.l=NIL THEN n := n.r; h := TRUE
  283.         ELSE
  284.           Q := n;
  285.           del(n.l);
  286.           n := New;
  287.           n.l := Q.l;
  288.           n.r := Q.r;
  289.           n.bal := Q.bal;
  290.           IF h THEN BalL(n) END
  291.         END;
  292.       END;
  293.     END;
  294.   END Rem;
  295.  
  296. BEGIN
  297.   h := FALSE; deleted := FALSE; Rem(root.root); RETURN deleted;
  298. END Remove;
  299.  
  300.  
  301. (*------  DoForward:  ------*)
  302.  
  303.  
  304. PROCEDURE DoForward* (root: Root;
  305.                       proc: DoProc);
  306.  
  307. (* ruft proc nacheinander (von links nach rechts) mit allen Elementen des Baumes
  308.    auf *)
  309.  
  310.   PROCEDURE DoFrwd(n: NodePtr);
  311.  
  312.   BEGIN
  313.     IF n#NIL THEN
  314.       DoFrwd(n.l);
  315.       proc(n);
  316.       DoFrwd(n.r);
  317.     END;
  318.   END DoFrwd;
  319.  
  320. BEGIN
  321.   DoFrwd(root.root);
  322. END DoForward;
  323.  
  324.  
  325. (*------  DoBackward:  ------*)
  326.  
  327.  
  328. PROCEDURE DoBackward* (root: Root;
  329.                        proc: DoProc);
  330.  
  331. (* ruft proc nacheinander rückwärts (von rechts nach links) mit allen
  332.    Elementen des Baumes auf *)
  333.  
  334.   PROCEDURE DoBkwd(n: NodePtr);
  335.  
  336.   BEGIN
  337.     IF n#NIL THEN
  338.       DoBkwd(n.r);
  339.       proc(n);
  340.       DoBkwd(n.l);
  341.     END;
  342.   END DoBkwd;
  343.  
  344. BEGIN
  345.   DoBkwd(root.root);
  346. END DoBackward;
  347.  
  348.  
  349. (*------  Dispose:  ------*)
  350.  
  351.  
  352. PROCEDURE Dispose* (VAR root: Root);
  353.  
  354. (* Ruft DISPOSE() mit allen Elementen des Baumes auf, gibt also seinen
  355.  * gesamten Speicher frei.
  356.  *)
  357.  
  358. (* $IFNOT GarbageCollector *)
  359.  
  360.   PROCEDURE Disp(n: NodePtr);
  361.  
  362.   BEGIN
  363.     IF n#NIL THEN
  364.       Disp(n.l);
  365.       Disp(n.r);
  366.       DISPOSE(n);
  367.     END;
  368.   END Disp;
  369. (* $END *)
  370.  
  371. BEGIN
  372. (* $IFNOT GarbageCollector *)
  373.   Disp(root.root);
  374. (* $END *)
  375.   root.root := NIL;
  376. END Dispose;
  377.  
  378.  
  379. (*-------------------------------------------------------------------------*)
  380.  
  381.  
  382. PROCEDURE (tree: RootPtr) Add        * (x: BT.ANY);
  383. (*
  384.  * add x to tree.
  385.  *)
  386. BEGIN
  387.   IF ~ Add(tree^,x(Node)) THEN HALT(20) END;
  388. END Add;
  389.  
  390.  
  391. PROCEDURE (tree: RootPtr) Remove     * (x: BT.ANY);
  392. (* removes x from tree.
  393.  *)
  394. BEGIN
  395.   IF ~ Remove(tree^,x(Node)) THEN HALT(20) END;
  396. END Remove;
  397.  
  398.  
  399. PROCEDURE (tree: RootPtr) nbElements * (): LONGINT;
  400. (* returns the number of elements within tree.
  401.  *)
  402.  
  403.   PROCEDURE CountElements(n: NodePtr): LONGINT;
  404.   BEGIN
  405.     IF n=NIL THEN RETURN 0
  406.              ELSE RETURN CountElements(n.l) + CountElements(n.r) + 1
  407.     END;
  408.   END CountElements;
  409.  
  410. BEGIN
  411.   RETURN CountElements(tree.root);
  412. END nbElements;
  413.  
  414.  
  415. PROCEDURE (tree: RootPtr) Do         * (p: BT.DoProc; par: BT.ANY);
  416. (* calls p(x,par) for every element x stored within tree.
  417.  * par passes some additional information to p. par is not touched by Do.
  418.  *)
  419.  
  420.   PROCEDURE Do(n: NodePtr);
  421.   BEGIN
  422.     IF n#NIL THEN
  423.       Do(n.l);
  424.       p(n,par);
  425.       Do(n.r);
  426.     END;
  427.   END Do;
  428.  
  429. BEGIN
  430.   Do(tree.root);
  431. END Do;
  432.  
  433.  
  434. (*-------------------------------------------------------------------------*)
  435.  
  436.  
  437. (*------  internes:  ------*)
  438.  
  439.  
  440. PROCEDURE * SCompProc(a,b: NodePtr): INTEGER;
  441.  
  442. BEGIN
  443.   WITH a: SNode DO
  444.     WITH b: SNode DO
  445.       IF    a.name>b.name THEN RETURN  1
  446.       ELSIF a.name<b.name THEN RETURN -1
  447.                           ELSE RETURN  0 END;
  448.     END;
  449.   END;
  450. END SCompProc;
  451.  
  452.  
  453. PROCEDURE * SFindProc(VAR root: Root; a: NodePtr): INTEGER;
  454.  
  455. BEGIN
  456.   WITH a: SNode DO
  457.     WITH root: SRoot DO
  458.       IF    a.name>root.findstr THEN RETURN  1
  459.       ELSIF a.name<root.findstr THEN RETURN -1
  460.                                 ELSE RETURN  0 END;
  461.     END;
  462.   END;
  463. END SFindProc;
  464.  
  465.  
  466. (*-------------------------------------------------------------------------*)
  467.  
  468.  
  469. (*------  SInit:  ------*)
  470.  
  471.  
  472. PROCEDURE SInit*(VAR root: SRoot);
  473.  
  474. (* initialisiert (leert) String-AVL-Baum und setzt die Vergleichsprozeduren
  475.    richtig. *)
  476.  
  477. BEGIN
  478.   Init(root,SCompProc,SFindProc);
  479. END SInit;
  480.  
  481.  
  482. (*------  SAdd:  ------*)
  483.  
  484.  
  485. PROCEDURE SAdd* (VAR root: SRoot;
  486.                      node: SNodePtr): BOOLEAN;
  487.  
  488. (* fügt node in den Baum ein. Das Ergebnis ist FALSE, wenn ein gleichnamiger
  489.    Knoten bereits im Baum enthalten ist, also nicht eingefügt werden
  490.    konnte. *)
  491.  
  492. BEGIN
  493.   RETURN Add(root,node);
  494. END SAdd;
  495.  
  496.  
  497. (*------  SFind:  ------*)
  498.  
  499.  
  500. PROCEDURE SFind*(VAR root: SRoot;
  501.                      str:  String): SNodePtr;
  502.  
  503. (* Sucht den Knoten mit dem Namen str und gibt seine Adresse oder NIL
  504.    bei Mißerfolg zurück. *)
  505.  
  506. VAR n: NodePtr;
  507.  
  508. BEGIN
  509.   root.findstr := str;
  510.   n := Find(root);
  511.   RETURN n(SNode);
  512. END SFind;
  513.  
  514.  
  515. (*-------------------------------------------------------------------------
  516.  
  517.   Bei String-AVL-Bäumen können die Prozeduren Remove, DoForward,
  518.   DoBackward und Dispose von oben verwendent werden. Sie sind deshalb
  519.   nicht noch einmal Implementiert.
  520.  
  521. -------------------------------------------------------------------------*)
  522.  
  523. END AVL.
  524.  
  525.  
  526.  
  527.  
  528.