{*********************************************************} {* SortFns *} {* Copyright (c) Julian M Bucknall 1998 *} {* KOL Portions ©2001-2005, Thaddy de Koning *} {* All rights reserved. *} {*********************************************************} {* Sort routines *} {*********************************************************} {Note: this unit is released as freeware. In other words, you are free to use this unit in your own applications, however I retain all copyright to the code. JMB} unit KOLSortFns; interface uses Kol; type TSortElement = double; TLessFunction = function (const X, Y : TSortElement) : boolean; {function prototype to compare two items and return true if item X is STRICTLY LESS than item Y} procedure BubbleSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure ShakerSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure SelectionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure InsertionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure ShellSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure QuickSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure UsualInsertionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); procedure UsualQuickSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); implementation procedure BubbleSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i, j : integer; Temp : TSortElement; begin for i := aLeft to pred(aRight) do for j := aRight downto succ(i) do if aLessThan(aItemArray[j], aItemArray[j-1]) then begin Temp := aItemArray[j]; aItemArray[j] := aItemArray[j-1]; aItemArray[j-1] := Temp; end; end; procedure ShakerSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i : integer; Temp : TSortElement; begin while (aLeft < aRight) do begin for i := aRight downto succ(aLeft) do if aLessThan(aItemArray[i], aItemArray[i-1]) then begin Temp := aItemArray[i]; aItemArray[i] := aItemArray[i-1]; aItemArray[i-1] := Temp; end; inc(aLeft); for i := succ(aLeft) to aRight do if aLessThan(aItemArray[i], aItemArray[i-1]) then begin Temp := aItemArray[i]; aItemArray[i] := aItemArray[i-1]; aItemArray[i-1] := Temp; end; dec(aRight); end; end; procedure SelectionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i, j : integer; IndexOfMin : integer; Temp : TSortElement; begin for i := aLeft to pred(aRight) do begin IndexOfMin := i; for j := succ(i) to aRight do if aLessThan(aItemArray[j], aItemArray[IndexOfMin]) then IndexOfMin := j; Temp := aItemArray[i]; aItemArray[i] := aItemArray[IndexOfMin]; aItemArray[IndexOfMin] := Temp; end; end; procedure UsualInsertionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i, j : integer; Temp : TSortElement; begin for i := succ(aLeft) to aRight do begin Temp := aItemArray[i]; j := i; while (j > aLeft) and aLessThan(Temp, aItemArray[j-1]) do begin aItemArray[j] := aItemArray[j-1]; dec(j); end; aItemArray[j] := Temp; end; end; procedure InsertionSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i, j : integer; IndexOfMin : integer; Temp : TSortElement; begin {find the smallest element and put it in the first position} IndexOfMin := aLeft; for i := succ(aLeft) to aRight do if aLessThan(aItemArray[i], aItemArray[IndexOfMin]) then IndexOfMin := i; if (aLeft <> IndexOfMin) then begin Temp := aItemArray[aLeft]; aItemArray[aLeft] := aItemArray[IndexOfMin]; aItemArray[IndexOfMin] := Temp; end; {now sort via insertion method} for i := aLeft+2 to aRight do begin Temp := aItemArray[i]; j := i; while aLessThan(Temp, aItemArray[j-1]) do begin aItemArray[j] := aItemArray[j-1]; dec(j); end; aItemArray[j] := Temp; end; end; procedure ShellSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); var i, j : integer; h : integer; Temp : TSortElement; begin {firstly calculate the first h value we shall use: it'll be about one ninth of the number of the elements} h := 1; while (h <= (aRight - aLeft) div 9) do h := (h * 3) + 1; {start a loop that'll decrement h by one third each time through} while (h > 0) do begin {now insertion sort each h-subfile} for i := (aLeft + h) to aRight do begin Temp := aItemArray[i]; j := i; while (j >= (aLeft+h)) and aLessThan(Temp, aItemArray[j-h]) do begin aItemArray[j] := aItemArray[j-h]; dec(j, h); end; aItemArray[j] := Temp; end; {decrease h by a third} h := h div 3; end; end; procedure UsualQuickSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); function Partition(L, R : integer): integer; var i, j : integer; Last : TSortElement; Temp : TSortElement; begin {set up the indexes} i := L; j := pred(R); {get the partition element} Last := aItemArray[R]; {do forever (we'll break out of the loop when needed)} while true do begin {find the first element greater than or equal to the partition element from the left; note that our partition element will stop this loop} while aLessThan(aItemArray[i], Last) do inc(i); {find the first element less than the partition element from the right; check to break out of the loop if we hit the left element - we have no sentinel there} while aLessThan(Last, aItemArray[j]) do begin if (j = L) then Break; dec(j); end; {if we crossed get out of this infinite loop to swap the partition element into place} if (i >= j) then Break; {otherwise swap the two out-of-place elements} Temp := aItemArray[i]; aItemArray[i] := aItemArray[j]; aItemArray[j] := Temp; {and continue} inc(i); dec(j); end; {swap the partition element into place, return the dividing index} aItemArray[R] := aItemArray[i]; aItemArray[i] := Last; Result := i; end; procedure QuickSortPrim(L, R : integer); var DividingItem : integer; begin {stop the recursion, if needed} if (R - L) <= 0 then Exit; {otherwise, partition about the final element in the set} DividingItem := Partition(L, R); {recursively quicksort the two subsets either side of the dividing element} QuicksortPrim(L, pred(DividingItem)); QuicksortPrim(succ(DividingItem), R); end; begin {start it all off} QuicksortPrim(aLeft, aRight); end; procedure QuickSort(var aItemArray : array of TSortElement; aLeft, aRight : integer; aLessThan : TLessFunction); function Partition(L, R : integer): integer; var i, j : integer; Last : TSortElement; Temp : TSortElement; begin {set up the indexes} i := L; j := pred(R); {get the partition element} Last := aItemArray[R]; {do forever (we'll break out of the loop when needed)} while true do begin {find the first element greater than or equal to the partition element from the left; note that our partition element will stop this loop} while aLessThan(aItemArray[i], Last) do inc(i); {find the first element less than the partition element from the right; note the median-of-three algorithm has ensured we have a sentinel on the left} while not aLessThan(aItemArray[j], Last) do dec(j); {if we crossed get out of this infinite loop to swap the partition element into place} if (i >= j) then Break; {otherwise swap the two out-of-place elements} Temp := aItemArray[i]; aItemArray[i] := aItemArray[j]; aItemArray[j] := Temp; {and continue} inc(i); dec(j); end; {swap the partition element into place, return the dividing index} aItemArray[R] := aItemArray[i]; aItemArray[i] := Last; Result := i; end; procedure QuickSortPrim(L, R : integer); var DividingItem : integer; Temp : TSortElement; i, j : integer; begin {if needed, stop the recursion at the cut-off point, and insertion sort} if (R - L) <= 10 then begin for i := succ(L) to R do begin Temp := aItemArray[i]; j := i; while (j > L) and aLessThan(Temp, aItemArray[j-1]) do begin aItemArray[j] := aItemArray[j-1]; dec(j); end; aItemArray[j] := Temp; end; Exit; end; {calculate the median-of-three element; for an extra bit of speed, put the smallest element of the three in the first position, the greatest in the last position, and the median in the last-but-one position and partition a smaller subset excluding the first and last} Temp := aItemArray[(L+R) shr 1]; aItemArray[(L+R) shr 1] := aItemArray[pred(R)]; aItemArray[pred(R)] := Temp; if not aLessThan(aItemArray[L], aItemArray[pred(R)]) then begin Temp := aItemArray[L]; aItemArray[L] := aItemArray[pred(R)]; aItemArray[pred(R)] := Temp; end; if not aLessThan(aItemArray[L], aItemArray[R]) then begin Temp := aItemArray[L]; aItemArray[L] := aItemArray[R]; aItemArray[R] := Temp; end; if not aLessThan(aItemArray[pred(R)], aItemArray[R]) then begin Temp := aItemArray[R]; aItemArray[R] := aItemArray[pred(R)]; aItemArray[pred(R)] := Temp; end; DividingItem := Partition(succ(L), pred(R)); {recursively quicksort the two subsets either side of the dividing element} QuickSortPrim(L, pred(DividingItem)); QuickSortPrim(succ(DividingItem), R); end; begin {start it all off} QuickSortPrim(aLeft, aRight); end; end.