home *** CD-ROM | disk | FTP | other *** search
- {*******************************************************************}
- { }
- { WVS Software Company }
- { Turbo Pascal Sorting Unit for TCollections }
- { Usage Fee: None, public domain }
- { Version: 1.0 }
- { Release Date: 6/27/93 }
- { }
- { Programmer: Brad Williams }
- { E-mail : bwilliams@marvin.ag.uidaho.edu }
- { US Mail : 1008 E. 7th }
- { Moscow, Idaho 83843 }
- { }
- {*******************************************************************}
- { }
- { This unit contains objects for performing various types of }
- { sorts. To use any of the sorting methods, simply pass them a }
- { collection and a compare or test function. You can write your }
- { programs to accept a TSortProcedure/TSearchFunction as a }
- { parameter to any function or procedure and use whichever type }
- { of sort/search you require at that point in your program. The }
- { search and sort methods accept pointers to compare and test }
- { functions so that the same functions can be used for iterative }
- { procedures/functions in a TSortedCollection. }
- { }
- {*******************************************************************}
- UNIT TVSorts;
- {****************************************************************************}
- INTERFACE
- {****************************************************************************}
- USES Objects;
-
- TYPE
- TCompareFunction = FUNCTION (Item1, Item2 : Pointer) : Integer;
- { A TCompareFunction must return: }
- { 1 if the Item1 > Item2 }
- { 0 if the Item1 = Item2 }
- { -1 if the Item1 < Item2 }
-
- TSortProcedure = PROCEDURE (ACollection : PCollection;
- Compare : TCompareFunction);
-
- { Sort Procedures }
- PROCEDURE BinaryInsertionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- PROCEDURE BubbleSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE CombSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE HeapSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE QuickSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE QuickSortNonRecursive (ACollection : PCollection;
- Compare : TCompareFunction);
- PROCEDURE ShakerSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE ShellSort (ACollection : PCollection; Compare : TCompareFunction);
- PROCEDURE StraightInsertionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- PROCEDURE StraightSelectionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- PROCEDURE TreeSort (ACollection : PCollection; Compare : TCompareFunction);
-
-
- { Compare Procedures - Must write your own Compare for pointer variables. }
- { This allows one sort routine to be used on any array. }
- FUNCTION CompareChars (Item1, Item2 : Pointer) : Integer; FAR;
- FUNCTION CompareInts (Item1, Item2 : Pointer) : Integer; FAR;
- FUNCTION CompareLongInts (Item1, Item2 : Pointer) : Integer; FAR;
- FUNCTION CompareReals (Item1, Item2 : Pointer) : Integer; FAR;
- FUNCTION CompareStrs (Item1, Item2 : Pointer) : Integer; FAR;
-
- {****************************************************************************}
- IMPLEMENTATION
- {****************************************************************************}
- { }
- { Local Procedures and Functions }
- { }
- {****************************************************************************}
- PROCEDURE Swap (ACollection : PCollection; A, B : Integer);
- VAR Item : Pointer;
- BEGIN
- Item := ACollection^.At(A);
- ACollection^.AtPut(A,ACollection^.At(B));
- ACollection^.AtPut(B,Item);
- END;
- {****************************************************************************}
- { }
- { Global Procedures and Functions }
- { }
- {****************************************************************************}
- PROCEDURE BinaryInsertionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- VAR i, j, Middle, Left, Right : LongInt;
- BEGIN
- FOR i := 0 TO (ACollection^.Count - 1) DO
- BEGIN
- Left := 0;
- Right := i;
- WHILE Left < Right DO
- BEGIN
- Middle := (Left + Right) DIV 2;
- WITH ACollection^ DO
- IF Compare(At(Middle),At(i)) < 1
- THEN Left := Middle + 1
- ELSE Right := Middle;
- END;
- FOR j := i DOWNTO (Right + 1) DO
- Swap(ACollection,j,j-1);
- END;
- END;
- {****************************************************************************}
- PROCEDURE BubbleSort (ACollection : PCollection; Compare : TCompareFunction);
- VAR i, j : Integer;
- BEGIN
- WITH ACollection^ DO
- FOR i := 1 TO (Count - 1) DO
- FOR j := (Count - 1) DOWNTO i DO
- IF Compare(At(j-1),At(j)) = 1
- THEN Swap(ACollection,j,j-1);
- END;
- {****************************************************************************}
- PROCEDURE CombSort (ACollection : PCollection; Compare : TCompareFunction);
- { The combsort is an optimised version of the bubble sort. It uses a }
- { decreasing gap in order to compare values of more than one element }
- { apart. By decreasing the gap the array is gradually "combed" into }
- { order ... like combing your hair. First you get rid of the large }
- { tangles, then the smaller ones ... }
- { }
- { There are a few particular things about the combsort. Firstly, the }
- { optimal shrink factor is 1.3 (worked out through a process of }
- { exhaustion by the guys at BYTE magazine). Secondly, by never }
- { having a gap of 9 or 10, but always using 11, the sort is faster. }
- { }
- { This sort approximates an n log n sort - it's faster than any }
- { other sort I've seen except the quicksort (and it beats that too }
- { sometimes ... have you ever seen a quicksort become an (n-1)^2 }
- { sort ... ?). The combsort does not slow down under *any* }
- { circumstances. In fact, on partially sorted lists (including }
- { *reverse* sorted lists) it speeds up. }
- { }
- { More information in the April 1991 BYTE magazine. }
- CONST ShrinkFactor = 1.3;
- VAR Gap, i : LongInt;
- Finished : Boolean;
- BEGIN
- Gap := Round((ACollection^.Count-1)/ShrinkFactor);
- WITH ACollection^ DO
- REPEAT
- Finished := TRUE;
- Gap := Trunc(Gap/ShrinkFactor);
- IF Gap < 1
- THEN Gap := 1
- ELSE IF ((Gap = 9) OR (Gap = 10))
- THEN Gap := 11;
- FOR i := 0 TO ((Count - 1) - Gap) DO
- IF Compare(At(i),At(i+Gap)) = 1
- THEN BEGIN
- Swap(ACollection,i,i+gap);
- Finished := False;
- END;
- UNTIL ((Gap = 1) AND Finished);
- END;
- {****************************************************************************}
- PROCEDURE HeapSort (ACollection : PCollection; Compare : TCompareFunction);
- { Performs best when items are in inverse order. }
- VAR L, R : LongInt;
- X : Pointer;
- {*****************************************}
- PROCEDURE Sift;
- VAR i, j : LongInt;
- Label 13;
- BEGIN
- i := L;
- j := 2 * i;
- X := ACollection^.At(i);
- WITH ACollection^ DO
- WHILE j <= R DO
- BEGIN
- IF j < R
- THEN IF Compare(At(j),At(j+1)) = -1
- THEN Inc(j);
- IF Compare(X,At(j)) >= 0
- THEN GoTo 13;
- AtPut(i,At(j));
- i := j;
- j := 2 * i;
- END;
- 13: ACollection^.AtPut(i,X);
- END;
- {*****************************************}
- BEGIN
- L := ((ACollection^.Count - 1) DIV 2) + 1;
- R := ACollection^.Count - 1;
- WHILE L > 0 DO
- BEGIN
- Dec(L);
- Sift;
- END;
- WHILE R > 0 DO
- BEGIN
- X := ACollection^.At(1);
- Swap(ACollection,0,R);
- Dec(R);
- Sift;
- END;
- END;
- {****************************************************************************}
- PROCEDURE QuickSort (ACollection : PCollection; Compare : TCompareFunction);
- {****************************************************************}
- PROCEDURE Sort (Left, Right : LongInt);
- VAR i, j : LongInt;
- X : Pointer;
- BEGIN
- WITH ACollection^ DO
- BEGIN
- i := Left;
- j := Right;
- X := At((Left + Right) DIV 2);
- REPEAT
- WHILE Compare(At(i),X) = -1 DO Inc(i);
- WHILE Compare(X,At(j)) = -1 DO Dec(j);
- IF i <= j
- THEN BEGIN
- Swap(ACollection,i,j);
- Inc(i);
- Dec(j)
- END;
- UNTIL i > j;
- IF Left < j
- THEN Sort(Left,j);
- IF i < Right
- THEN Sort(i,Right)
- END;
- END;
- {****************************************************************}
- BEGIN
- Sort(0,ACollection^.Count-1);
- END;
- {****************************************************************************}
- PROCEDURE QuickSortNonRecursive (ACollection : PCollection;
- Compare : TCompareFunction);
- CONST m = 12;
- VAR i, j, L, R : LongInt;
- x : Pointer;
- s : 0..m;
- Stack : ARRAY[1..m] OF RECORD
- l, r : LongInt;
- END;
- BEGIN
- s := 1;
- Stack[1].l := 0;
- Stack[1].r := ACollection^.Count - 1;
- WITH ACollection^ DO
- REPEAT
- L := Stack[s].l;
- R := Stack[s].r;
- Dec(S);
- REPEAT
- i := L;
- j := R;
- x := At((L + R) DIV 2);
- REPEAT
- WHILE Compare(x,At(i)) = 1 DO Inc(i);
- WHILE Compare(x,At(j)) = -1 DO Dec(j);
- IF i <= j
- THEN BEGIN
- Swap(ACollection,i,j);
- Inc(i);
- Dec(j);
- END;
- UNTIL i > j;
- IF i < R
- THEN BEGIN
- Inc(s);
- Stack[s].l := i;
- Stack[s].r := R;
- END;
- R := j;
- UNTIL L >= R;
- UNTIL s = 0;
- END;
- {****************************************************************************}
- PROCEDURE ShakerSort (ACollection : PCollection; Compare : TCompareFunction);
- { Works for any array and any index range. }
- VAR j, k, Left, Right : LongInt;
- BEGIN
- Left := 1;
- Right := (ACollection^.Count - 1);
- k := Right;
- WITH ACollection^ DO
- REPEAT
- FOR j := Right DOWNTO Left DO
- IF Compare(At(j-1),At(j)) = 1
- THEN BEGIN
- Swap(ACollection,j,j-1);
- k := j;
- END;
- Left := k + 1;
- FOR j := Left TO Right DO
- IF Compare(At(j-1),At(j)) = 1
- THEN BEGIN
- Swap(ACollection,j,j-1);
- k := j;
- END;
- Right := k - 1;
- UNTIL Left > Right;
- END;
- {****************************************************************************}
- PROCEDURE ShellSort (ACollection : PCollection; Compare : TCompareFunction);
- VAR Gap, i, j, k : LongInt;
- BEGIN
- Gap := (ACollection^.Count - 1) DIV 2;
- WHILE (Gap > 0) DO
- BEGIN
- FOR i := Gap TO (ACollection^.Count - 1) DO
- BEGIN
- j := i - Gap;
- WHILE (j > -1) DO
- BEGIN
- k := j + Gap;
- IF Compare(ACollection^.At(j),ACollection^.At(k)) < 1
- THEN j := 0
- ELSE Swap(ACollection,j,k);
- Dec(j,Gap);
- END;
- END;
- Gap := Gap DIV 2;
- END;
- END;
- {****************************************************************************}
- PROCEDURE StraightInsertionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- VAR i, j : LongInt;
- X : Pointer;
- BEGIN
- WITH ACollection^ DO
- FOR i := 0 TO (Count - 1) DO
- BEGIN
- X := At(i);
- j := i;
- WHILE (j > 0) AND (Compare(X,At(j-1)) = -1) DO
- BEGIN
- AtPut(j,At(j-1));
- Dec(j);
- END;
- AtPut(j,X);
- END;
- END;
- {****************************************************************************}
- PROCEDURE StraightSelectionSort (ACollection : PCollection;
- Compare : TCompareFunction);
- VAR i, j, k : LongInt;
- BEGIN
- FOR i := 0 TO (ACollection^.Count - 1) DO
- BEGIN
- k := i;
- FOR j := (i + 1) TO (ACollection^.Count - 1) DO
- IF Compare(ACollection^.At(j),ACollection^.At(k)) = -1
- THEN k := j;
- Swap(ACollection,i,k);
- END;
- END;
- {****************************************************************************}
- PROCEDURE TreeSort (ACollection : PCollection; Compare : TCompareFunction);
- {after D.Cooke, A.H.Craven, G.M.Clarke: Statistical Computing
- in Pascal, Publisher: Edward Arnold, London 1985 ISBN 0-7131-3545-X }
- TYPE PNode = ^Node;
- Node = RECORD
- Value : Pointer;
- Left : PNode;
- Right : PNode;
- END;
- VAR Add, Top : PNode;
- i : LongInt;
- {***********************************************************}
- PROCEDURE MakeTree (VAR Node : PNode);
- BEGIN
- IF Node = NIL
- THEN Node := Add
- ELSE IF Compare(Add^.Value,Node^.Value) = 1
- THEN MakeTree(Node^.Right)
- ELSE MakeTree(Node^.Left);
- END;
- {**********************************************************}
- PROCEDURE StripTree (Node : PNode);
- BEGIN
- IF Node <> NIL
- THEN BEGIN
- StripTree(Node^.Left);
- ACollection^.AtPut(i,Node^.Value);
- Inc(i);
- StripTree(Node^.Right)
- END;
- END;
- {**********************************************************}
- BEGIN
- Top := NIL;
- FOR i := 0 TO (ACollection^.Count - 1) DO
- BEGIN
- New(Add);
- Add^.Value := ACollection^.At(i);
- Add^.Left := NIL;
- Add^.Right := NIL;
- MakeTree(Top)
- END;
- i := 0;
- StripTree(Top)
- END;
- {****************************************************************************}
- { }
- { Compare Procedures }
- { }
- {****************************************************************************}
- FUNCTION CompareChars (Item1, Item2 : Pointer) : Integer;
- BEGIN
- IF Char(Item1^) < Char(Item2^)
- THEN CompareChars := -1
- ELSE CompareChars := Ord(Char(Item1^) <> Char(Item2^));
- END;
- {*****************************************************************************}
- FUNCTION CompareInts (Item1, Item2 : Pointer) : Integer;
- BEGIN
- IF Integer(Item1^) < Integer(Item2^)
- THEN CompareInts := -1
- ELSE CompareInts := Ord(Integer(Item1^) <> Integer(Item2^));
- END;
- {*****************************************************************************}
- FUNCTION CompareLongInts (Item1, Item2 : Pointer) : Integer;
- BEGIN
- IF LongInt(Item1^) < LongInt(Item2^)
- THEN CompareLongInts := -1
- ELSE CompareLongInts := Ord(LongInt(Item1^) <> LongInt(Item2^));
- END;
- {*****************************************************************************}
- FUNCTION CompareReals (Item1, Item2 : Pointer) : Integer;
- BEGIN
- IF Real(Item1^) < Real(Item2^)
- THEN CompareReals := -1
- ELSE CompareReals := Ord(Real(Item1^) <> Real(Item2^));
- END;
- {*****************************************************************************}
- FUNCTION CompareStrs (Item1, Item2 : Pointer) : Integer;
- BEGIN
- IF String(Item1^) < String(Item2^)
- THEN CompareStrs := -1
- ELSE CompareStrs := Ord(String(Item1^) <> String(Item2^));
- END;
- {*****************************************************************************}
- BEGIN
- END.
-
- { ----------------------------------- DEMO PROGRAM ---------------------}
-
- PROGRAM Test;
- USES Crt, Objects, TVSorts;
-
- CONST
- MaxCollectionSize = 10;
-
- VAR C : TCollection;
- i, j, k : Integer;
- Ch : ^Char;
-
- BEGIN
- Randomize;
- FOR i := 1 TO 11 DO
- BEGIN
- { initialize collection and load with data in reverse order }
- C.Init(MaxCollectionSize,1);
- FOR j := MaxCollectionSize DOWNTO 0 DO
- BEGIN
- k := Random(255);
- WHILE (k < 65) OR (k > 90) DO k := Random(255);
- New(Ch);
- Ch^ := Char(k);
- C.AtInsert(0,Ch);
- END;
- { display unsorted data }
- ClrScr;
- CASE i OF
- 1 : WriteLn('Binary Insertion Sort');
- 2 : WriteLn('Bubble Sort');
- 3 : WriteLn('Comb Sort');
- 4 : WriteLn('Heap Sort');
- 5 : WriteLn('Quick Sort');
- 6 : WriteLn('Non-recursive Quick Sort');
- 7 : WriteLn('Shaker Sort');
- 8 : WriteLn('Shell Sort');
- 9 : WriteLn('Straight Insertion Sort');
- 10 : WriteLn('Straight Selection Sort');
- 11 : WriteLn('Tree Sort');
- END;
- FOR j := 0 TO (C.Count - 1) DO Write(Char(C.At(j)^):2);
- { sort data }
- CASE i OF
- 1 : BinaryInsertionSort(@C,CompareChars);
- 2 : BubbleSort(@C,CompareChars);
- 3 : CombSort(@C,CompareChars);
- 4 : HeapSort(@C,CompareChars);
- 5 : QuickSort(@C,CompareChars);
- 6 : QuickSortNonRecursive(@C,CompareChars);
- 7 : ShakerSort(@C,CompareChars);
- 8 : ShellSort(@C,CompareChars);
- 9 : StraightInsertionSort(@C,CompareChars);
- 10 : StraightSelectionSort(@C,CompareChars);
- 11 : TreeSort(@C,CompareChars);
- END;
- { display sorted data }
- WriteLn;
- FOR j := 0 TO (C.Count - 1) DO Write(Char(C.At(j)^):2);
- ReadLn;
- { clear of collection }
- END;
- END.