|
@@ -37,6 +37,7 @@ unit Generics.Collections;
|
|
|
{$HINTS OFF}
|
|
|
{$OVERFLOWCHECKS OFF}
|
|
|
{$RANGECHECKS OFF}
|
|
|
+{$POINTERMATH ON}
|
|
|
|
|
|
interface
|
|
|
|
|
@@ -70,7 +71,6 @@ type
|
|
|
// bug #24282
|
|
|
TComparerBugHack = TComparer<T>;
|
|
|
protected
|
|
|
- // modified QuickSort from classes\lists.inc
|
|
|
class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>);
|
|
|
virtual; abstract;
|
|
|
public
|
|
@@ -97,8 +97,14 @@ type
|
|
|
end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TCustomArray (bug #24254)
|
|
|
|
|
|
TArrayHelper<T> = class(TCustomArrayHelper<T>)
|
|
|
+ private
|
|
|
+ type
|
|
|
+ PT = ^T;
|
|
|
+ class procedure QSort(p: PT; n, reasonable: SizeUint; const cmp: IComparer<T>); static;
|
|
|
+ class function Median(p: PT; n: SizeUint; const cmp: IComparer<T>): PT; static;
|
|
|
+ class procedure HeapSort(p: PT; n: SizeUint; const cmp: IComparer<T>); static;
|
|
|
+ class procedure HeapReplacePessimistic(q: PT; nq, id: SizeUint; const item: T; const cmp: IComparer<T>); static;
|
|
|
protected
|
|
|
- // modified QuickSort from classes\lists.inc
|
|
|
class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>); override;
|
|
|
public
|
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
@@ -1004,51 +1010,123 @@ end;
|
|
|
|
|
|
{ TArrayHelper<T> }
|
|
|
|
|
|
-class procedure TArrayHelper<T>.QuickSort(var AValues: array of T; ALeft, ARight: SizeInt;
|
|
|
- const AComparer: IComparer<T>);
|
|
|
+class procedure TArrayHelper<T>.QSort(p: PT; n, reasonable: SizeUint; const cmp: IComparer<T>);
|
|
|
var
|
|
|
- I, J: SizeInt;
|
|
|
- P, Q: T;
|
|
|
+ L, R: SizeInt;
|
|
|
+ pivot, temp: T;
|
|
|
begin
|
|
|
- if ((ARight - ALeft) <= 0) or (Length(AValues) = 0) then
|
|
|
- Exit;
|
|
|
- repeat
|
|
|
- I := ALeft;
|
|
|
- J := ARight;
|
|
|
- P := AValues[ALeft + (ARight - ALeft) shr 1];
|
|
|
+ while (n >= 2) and (reasonable > 0) do
|
|
|
+ begin
|
|
|
+ { 'reasonable' loses 3/16 (~20%) on each partition, and on reaching zero, heap sort is performed.
|
|
|
+ This means -log13/16(n) ~=~ 3.3 * log2(n) partitions allowed. }
|
|
|
+ reasonable := reasonable div 2 + reasonable div 4 + reasonable div 16;
|
|
|
+ pivot := Median(p, n, cmp)^;
|
|
|
+
|
|
|
+ R := 0;
|
|
|
+ L := n - 1;
|
|
|
repeat
|
|
|
- while AComparer.Compare(AValues[I], P) < 0 do
|
|
|
- Inc(I);
|
|
|
- while AComparer.Compare(AValues[J], P) > 0 do
|
|
|
- Dec(J);
|
|
|
- if I <= J then
|
|
|
+ while cmp.Compare((p + R)^, pivot) < 0 do
|
|
|
+ inc(R);
|
|
|
+ while cmp.Compare(pivot, (p + L)^) < 0 do
|
|
|
+ dec(L);
|
|
|
+ if R <= L then
|
|
|
begin
|
|
|
- if I <> J then
|
|
|
- begin
|
|
|
- Q := AValues[I];
|
|
|
- AValues[I] := AValues[J];
|
|
|
- AValues[J] := Q;
|
|
|
- end;
|
|
|
- Inc(I);
|
|
|
- Dec(J);
|
|
|
+ temp := (p + R)^; (p + R)^ := (p + L)^; (p + L)^ := temp;
|
|
|
+ inc(R);
|
|
|
+ dec(L);
|
|
|
end;
|
|
|
- until I > J;
|
|
|
- // sort the smaller range recursively
|
|
|
- // sort the bigger range via the loop
|
|
|
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
|
|
|
- if J - ALeft < ARight - I then
|
|
|
+ until R > L;
|
|
|
+
|
|
|
+ { [0 .. L], [R .. n - 1]. Possible edge cases are L = -1 or R = n. Recurse into the smaller half. }
|
|
|
+ if n - R <= L then
|
|
|
begin
|
|
|
- if ALeft < J then
|
|
|
- QuickSort(AValues, ALeft, J, AComparer);
|
|
|
- ALeft := I;
|
|
|
- end
|
|
|
- else
|
|
|
+ QSort(p + R, n - R, reasonable, cmp);
|
|
|
+ n := L + 1;
|
|
|
+ end else
|
|
|
begin
|
|
|
- if I < ARight then
|
|
|
- QuickSort(AValues, I, ARight, AComparer);
|
|
|
- ARight := J;
|
|
|
+ QSort(p, L + 1, reasonable, cmp);
|
|
|
+ p := p + R;
|
|
|
+ n := n - R;
|
|
|
end;
|
|
|
- until ALeft >= ARight;
|
|
|
+ end;
|
|
|
+ if n >= 2 then
|
|
|
+ HeapSort(p, n, cmp);
|
|
|
+end;
|
|
|
+
|
|
|
+class function TArrayHelper<T>.Median(p: PT; n: SizeUint; const cmp: IComparer<T>): PT;
|
|
|
+var
|
|
|
+ a, b, c, temp: PT;
|
|
|
+begin
|
|
|
+ a := p;
|
|
|
+ b := p + n div 2;
|
|
|
+ c := p + (n - 1);
|
|
|
+ if cmp.Compare(b^, a^) < 0 then begin temp := a; a := b; b := temp; end;
|
|
|
+ if cmp.Compare(c^, b^) < 0 then begin temp := b; b := c; c := temp; end;
|
|
|
+ if cmp.Compare(b^, a^) < 0 then result := a else result := b;
|
|
|
+end;
|
|
|
+
|
|
|
+class procedure TArrayHelper<T>.HeapSort(p: PT; n: SizeUint; const cmp: IComparer<T>);
|
|
|
+var
|
|
|
+ temp: T;
|
|
|
+ i: SizeInt;
|
|
|
+begin
|
|
|
+ for i := SizeUint(n - 2) div 2 downto 0 do
|
|
|
+ begin
|
|
|
+ temp := (p + i)^;
|
|
|
+ HeapReplacePessimistic(p, n, i, temp, cmp);
|
|
|
+ end;
|
|
|
+
|
|
|
+ for i := n - 1 downto 1 do
|
|
|
+ begin
|
|
|
+ temp := (p + i)^;
|
|
|
+ (p + i)^ := p^;
|
|
|
+ HeapReplacePessimistic(p, i, 0, temp, cmp);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ HeapReplacePessimistic replaces q[id] with 'item' by doing something like
|
|
|
+
|
|
|
+ startId := id;
|
|
|
+ q[id] := item;
|
|
|
+ id := HeapDownThoroughly(q, nq, id);
|
|
|
+ id := HeapUpToId(q, nq, id, startId);
|
|
|
+
|
|
|
+ Where 'HeapDownThoroughly' sinks the element all the way down, without stopping at the correct position, so it must float up afterwards.
|
|
|
+ See Python's 'heapq' module for explanation why this is an improvement over simple HeapDown.
|
|
|
+ TL;DR: HeapDownThoroughly uses 1 fewer comparison per level, and the item usually ends up close to the bottom, so these savings pay off.
|
|
|
+
|
|
|
+ Moreover, heap invariant assumed for q[id .. nq - 1] rather than whole q[0 .. nq - 1] which matters when heapifying the array from the end. }
|
|
|
+
|
|
|
+class procedure TArrayHelper<T>.HeapReplacePessimistic(q: PT; nq, id: SizeUint; const item: T; const cmp: IComparer<T>);
|
|
|
+var
|
|
|
+ iChild, iParent, start: SizeUint;
|
|
|
+begin
|
|
|
+ start := id;
|
|
|
+ repeat
|
|
|
+ iChild := 2 * id + 1; { childs of q[id] are q[2 * id + 1] ... q[2 * id + 2]. }
|
|
|
+ if iChild >= nq then
|
|
|
+ break;
|
|
|
+ if (iChild + 1 < nq) and (cmp.Compare((q + iChild)^, (q + iChild + 1)^) < 0) then
|
|
|
+ iChild := iChild + 1;
|
|
|
+ (q + id)^ := (q + iChild)^;
|
|
|
+ id := iChild;
|
|
|
+ until false;
|
|
|
+
|
|
|
+ while id > start do
|
|
|
+ begin
|
|
|
+ iParent := SizeUint(id - 1) div 2;
|
|
|
+ if cmp.Compare((q + iParent)^, item) >= 0 then
|
|
|
+ break;
|
|
|
+ (q + id)^ := (q + iParent)^;
|
|
|
+ id := iParent;
|
|
|
+ end;
|
|
|
+ (q + id)^ := item;
|
|
|
+end;
|
|
|
+
|
|
|
+class procedure TArrayHelper<T>.QuickSort(var AValues: array of T; ALeft, ARight: SizeInt;
|
|
|
+ const AComparer: IComparer<T>);
|
|
|
+begin
|
|
|
+ QSort(PT(AValues) + ALeft, ARight - ALeft + 1, ARight - ALeft + 1, AComparer);
|
|
|
end;
|
|
|
|
|
|
class function TArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|