235 lines
5.3 KiB
ObjectPascal
235 lines
5.3 KiB
ObjectPascal
unit PQueue;
|
|
{$mode objfpc}{$h+}{$b-}
|
|
interface
|
|
uses
|
|
SysUtils;
|
|
|
|
type
|
|
EPqError = class(Exception);
|
|
|
|
generic TPriorityQueue<T> = class
|
|
public
|
|
type
|
|
TComparer = function(const L, R: T): Boolean;
|
|
THandle = type SizeInt;
|
|
const
|
|
NULL_HANDLE = THandle(-1);
|
|
strict private
|
|
type
|
|
TNode = record
|
|
Data: T;
|
|
HeapIndex: SizeInt;
|
|
end;
|
|
const
|
|
INIT_SIZE = 16;
|
|
NULL_INDEX = SizeInt(-1);
|
|
SEUndefComparer = 'Undefined comparer';
|
|
SEInvalidHandleFmt = 'Invalid handle value(%d)';
|
|
SEAccessEmpty = 'Cannot access an empty queue item';
|
|
var
|
|
FNodes: array of TNode;
|
|
FHeap: array of SizeInt;
|
|
FCount,
|
|
FStackTop: SizeInt;
|
|
FCompare: TComparer;
|
|
procedure CheckEmpty;
|
|
procedure Expand;
|
|
function NodeAdd(const aValue: T; aIndex: SizeInt): SizeInt;
|
|
function NodeRemove(aIndex: SizeInt): T;
|
|
function StackPop: SizeInt;
|
|
procedure StackPush(aIdx: SizeInt);
|
|
procedure PushUp(Idx: SizeInt);
|
|
procedure SiftDown(Idx: SizeInt);
|
|
function DoPop: T;
|
|
public
|
|
constructor Create(c: TComparer);
|
|
function IsEmpty: Boolean;
|
|
procedure Clear;
|
|
function Push(const v: T): THandle;
|
|
function Pop: T;
|
|
function TryPop(out v: T): Boolean;
|
|
function Peek: T;
|
|
function TryPeek(out v: T): Boolean;
|
|
function GetValue(h: THandle): T;
|
|
procedure Update(h: THandle; const v: T);
|
|
property Count: SizeInt read FCount;
|
|
end;
|
|
|
|
implementation
|
|
|
|
procedure TPriorityQueue.CheckEmpty;
|
|
begin
|
|
if Count = 0 then raise EPqError.Create(SEAccessEmpty);
|
|
end;
|
|
|
|
procedure TPriorityQueue.Expand;
|
|
begin
|
|
if Length(FHeap) < INIT_SIZE then begin
|
|
SetLength(FHeap, INIT_SIZE);
|
|
SetLength(FNodes, INIT_SIZE)
|
|
end
|
|
else begin
|
|
SetLength(FHeap, Length(FHeap) * 2);
|
|
SetLength(FNodes, Length(FNodes) * 2);
|
|
end;
|
|
end;
|
|
|
|
function TPriorityQueue.NodeAdd(const aValue: T; aIndex: SizeInt): SizeInt;
|
|
begin
|
|
if FStackTop <> NULL_INDEX then
|
|
Result := StackPop
|
|
else
|
|
Result := FCount;
|
|
FNodes[Result].Data := aValue;
|
|
FNodes[Result].HeapIndex := aIndex;
|
|
Inc(FCount);
|
|
end;
|
|
|
|
function TPriorityQueue.NodeRemove(aIndex: SizeInt): T;
|
|
begin
|
|
StackPush(aIndex);
|
|
Result := FNodes[aIndex].Data;
|
|
end;
|
|
|
|
function TPriorityQueue.StackPop: SizeInt;
|
|
begin
|
|
Result := FStackTop;
|
|
if Result <> NULL_INDEX then begin
|
|
FStackTop := FNodes[Result].HeapIndex;
|
|
FNodes[Result].HeapIndex := NULL_INDEX;
|
|
end;
|
|
end;
|
|
|
|
procedure TPriorityQueue.StackPush(aIdx: SizeInt);
|
|
begin
|
|
FNodes[aIdx].HeapIndex := FStackTop;
|
|
FStackTop := aIdx;
|
|
end;
|
|
|
|
procedure TPriorityQueue.PushUp(Idx: SizeInt);
|
|
var
|
|
Prev, Curr: SizeInt;
|
|
begin
|
|
Prev := (Idx - 1) shr 1;
|
|
Curr := FHeap[Idx];
|
|
while(Idx > 0) and FCompare(FNodes[FHeap[Prev]].Data, FNodes[Curr].Data) do begin
|
|
FHeap[Idx] := FHeap[Prev];
|
|
FNodes[FHeap[Prev]].HeapIndex := Idx;
|
|
Idx := Prev;
|
|
Prev := (Prev - 1) shr 1;
|
|
end;
|
|
FHeap[Idx] := Curr;
|
|
FNodes[Curr].HeapIndex := Idx;
|
|
end;
|
|
|
|
procedure TPriorityQueue.SiftDown(Idx: SizeInt);
|
|
var
|
|
Next, Sifted: SizeInt;
|
|
begin
|
|
if Count < 2 then exit;
|
|
Next := Idx*2 + 1;
|
|
Sifted := FHeap[Idx];
|
|
while Next < Count do begin
|
|
if(Next+1 < Count)and FCompare(FNodes[FHeap[Next]].Data, FNodes[FHeap[Next+1]].Data)then Inc(Next);
|
|
if not FCompare(FNodes[Sifted].Data, FNodes[FHeap[Next]].Data) then break;
|
|
FHeap[Idx] := FHeap[Next];
|
|
FNodes[FHeap[Next]].HeapIndex := Idx;
|
|
Idx := Next;
|
|
Next := Next*2 + 1;
|
|
end;
|
|
FHeap[Idx] := Sifted;
|
|
FNodes[Sifted].HeapIndex := Idx;
|
|
end;
|
|
|
|
function TPriorityQueue.DoPop: T;
|
|
begin
|
|
Result := NodeRemove(FHeap[0]);
|
|
Dec(FCount);
|
|
if Count > 0 then begin
|
|
FHeap[0] := FHeap[Count];
|
|
SiftDown(0);
|
|
end;
|
|
end;
|
|
|
|
constructor TPriorityQueue.Create(c: TComparer);
|
|
begin
|
|
if c = nil then raise EPqError.Create(SEUndefComparer);
|
|
FCompare := c;
|
|
FStackTop := NULL_INDEX;
|
|
end;
|
|
|
|
function TPriorityQueue.IsEmpty: Boolean;
|
|
begin
|
|
Result := Count = 0;
|
|
end;
|
|
|
|
procedure TPriorityQueue.Clear;
|
|
begin
|
|
FNodes := nil;
|
|
FHeap := nil;
|
|
FCount := 0;
|
|
FStackTop := NULL_INDEX;
|
|
end;
|
|
|
|
function TPriorityQueue.Push(const v: T): THandle;
|
|
var
|
|
InsertPos: SizeInt;
|
|
begin
|
|
if Count = Length(FHeap) then Expand;
|
|
InsertPos := Count;
|
|
Result := NodeAdd(v, InsertPos);
|
|
FHeap[InsertPos] := Result;
|
|
if InsertPos > 0 then PushUp(InsertPos);
|
|
end;
|
|
|
|
function TPriorityQueue.Pop: T;
|
|
begin
|
|
CheckEmpty;
|
|
Result := DoPop;
|
|
end;
|
|
|
|
function TPriorityQueue.TryPop(out v: T): Boolean;
|
|
begin
|
|
if Count = 0 then exit(False);
|
|
v := DoPop;
|
|
Result := True;
|
|
end;
|
|
|
|
function TPriorityQueue.Peek: T;
|
|
begin
|
|
CheckEmpty;
|
|
Result := FNodes[FHeap[0]].Data;
|
|
end;
|
|
|
|
function TPriorityQueue.TryPeek(out v: T): Boolean;
|
|
begin
|
|
if Count = 0 then exit(False);
|
|
v := FNodes[FHeap[0]].Data;
|
|
Result := True;
|
|
end;
|
|
|
|
function TPriorityQueue.GetValue(h: THandle): T;
|
|
begin
|
|
if SizeUInt(h) < SizeUInt(Length(FHeap)) then
|
|
Result := FNodes[h].Data
|
|
else
|
|
raise EPqError.CreateFmt(SEInvalidHandleFmt, [h]);
|
|
end;
|
|
|
|
procedure TPriorityQueue.Update(h: THandle; const v: T);
|
|
begin
|
|
if SizeUInt(h) < SizeUInt(Length(FHeap)) then begin
|
|
if FCompare(FNodes[h].Data, v) then begin
|
|
FNodes[h].Data := v;
|
|
PushUp(FNodes[h].HeapIndex);
|
|
end else
|
|
if FCompare(v, FNodes[h].Data) then begin
|
|
FNodes[h].Data := v;
|
|
SiftDown(FNodes[h].HeapIndex);
|
|
end;
|
|
end else
|
|
raise EPqError.CreateFmt(SEInvalidHandleFmt, [h]);
|
|
end;
|
|
|
|
end.
|