mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-03 18:18:37 +02:00
429 lines
9.8 KiB
ObjectPascal
429 lines
9.8 KiB
ObjectPascal
unit dbf_avl;
|
|
|
|
interface
|
|
|
|
{$I dbf_common.inc}
|
|
|
|
uses
|
|
Dbf_Common;
|
|
|
|
type
|
|
TBal = -1..1;
|
|
|
|
TAvlTree = class;
|
|
|
|
TKeyType = Cardinal;
|
|
TExtraData = Pointer;
|
|
|
|
PData = ^TData;
|
|
TData = record
|
|
ID: TKeyType;
|
|
ExtraData: TExtraData;
|
|
end;
|
|
|
|
PNode = ^TNode;
|
|
TNode = record
|
|
Data: TData;
|
|
Left: PNode;
|
|
Right: PNode;
|
|
Bal: TBal; // balance factor: h(Right) - h(Left)
|
|
end;
|
|
|
|
TAvlTreeEvent = procedure(Sender: TAvlTree; Data: PData) of object;
|
|
|
|
TAvlTree = class(TObject)
|
|
private
|
|
FRoot: PNode;
|
|
FCount: Cardinal;
|
|
FOnDelete: TAvlTreeEvent;
|
|
FHeightChange: Boolean;
|
|
|
|
procedure InternalInsert(X: PNode; var P: PNode);
|
|
procedure InternalDelete(X: TKeyType; var P: PNode);
|
|
|
|
procedure DeleteNode(X: PNode);
|
|
procedure TreeDispose(X: PNode);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
procedure Clear;
|
|
function Find(Key: TKeyType): TExtraData;
|
|
procedure Insert(Key: TKeyType; Extra: TExtraData);
|
|
procedure Delete(Key: TKeyType);
|
|
|
|
function Lowest: PData;
|
|
|
|
property Count: Cardinal read FCount;
|
|
property OnDelete: TAvlTreeEvent read FOnDelete write FOnDelete;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math;
|
|
|
|
procedure RotL(var P: PNode);
|
|
var
|
|
P1: PNode;
|
|
begin
|
|
P1 := P^.Right;
|
|
P^.Right := P1^.Left;
|
|
P1^.Left := P;
|
|
P := P1;
|
|
end;
|
|
|
|
procedure RotR(var P: PNode);
|
|
var
|
|
P1: PNode;
|
|
begin
|
|
P1 := P^.Left;
|
|
P^.Left := P1^.Right;
|
|
P1^.Right := P;
|
|
P := P1;
|
|
end;
|
|
|
|
function Height(X: PNode): Integer;
|
|
begin
|
|
if X = nil then
|
|
Result := 0
|
|
else
|
|
Result := 1+Max(Height(X^.Left), Height(X^.Right));
|
|
end;
|
|
|
|
function CheckTree_T(X: PNode; var H: Integer): Boolean;
|
|
var
|
|
HR: Integer;
|
|
begin
|
|
if X = nil then
|
|
begin
|
|
Result := true;
|
|
H := 0;
|
|
end else begin
|
|
Result := CheckTree_T(X^.Left, H) and CheckTree_T(X^.Right, HR) and
|
|
((X^.Left = nil) or (X^.Left^.Data.ID < X^.Data.ID)) and
|
|
((X^.Right = nil) or (X^.Right^.Data.ID > X^.Data.ID)) and
|
|
// ((Height(X^.Right) - Height(X^.Left)) = X^.Bal);
|
|
(HR - H = X^.Bal);
|
|
H := 1 + Max(H, HR);
|
|
end;
|
|
end;
|
|
|
|
function CheckTree(X: PNode): Boolean;
|
|
var
|
|
H: Integer;
|
|
begin
|
|
Result := CheckTree_T(X, H);
|
|
end;
|
|
|
|
procedure BalanceLeft(var P: PNode; var HeightChange: Boolean);
|
|
var
|
|
B1, B2: TBal;
|
|
{HeightChange = true, left branch has become less high}
|
|
begin
|
|
case P^.Bal of
|
|
-1: begin P^.Bal := 0 end;
|
|
0: begin P^.Bal := 1; HeightChange := false end;
|
|
1: begin {Rebalance}
|
|
B1 := P^.Right^.Bal;
|
|
if B1 >= 0
|
|
then {single L rotation}
|
|
begin
|
|
RotL(P);
|
|
//adjust balance factors:
|
|
if B1 = 0
|
|
then
|
|
begin P^.Bal :=-1; P^.Left^.Bal := 1; HeightChange := false end
|
|
else
|
|
begin P^.Bal := 0; P^.Left^.Bal := 0 end;
|
|
end
|
|
else {double RL rotation}
|
|
begin
|
|
B2 := P^.Right^.Left^.Bal;
|
|
RotR(P^.Right);
|
|
RotL(P);
|
|
//adjust balance factors:
|
|
if B2=+1 then P^.Left^.Bal := -1 else P^.Left^.Bal := 0;
|
|
if B2=-1 then P^.Right^.Bal := 1 else P^.Right^.Bal := 0;
|
|
P^.Bal := 0;
|
|
end;
|
|
end;{1}
|
|
end{case}
|
|
end;{BalanceLeft}
|
|
|
|
procedure BalanceRight(var P: PNode; var HeightChange: Boolean);
|
|
var
|
|
B1, B2: TBal;
|
|
{HeightChange = true, right branch has become less high}
|
|
begin
|
|
case P^.Bal of
|
|
1: begin P^.Bal := 0 end;
|
|
0: begin P^.Bal := -1; HeightChange := false end;
|
|
-1: begin {Rebalance}
|
|
B1 := P^.Left^.Bal;
|
|
if B1 <= 0
|
|
then {single R rotation}
|
|
begin
|
|
RotR(P);
|
|
//adjust balance factors}
|
|
if B1 = 0
|
|
then
|
|
begin P^.Bal :=1; P^.Right^.Bal :=-1; HeightChange:= false end
|
|
else
|
|
begin P^.Bal := 0; P^.Right^.Bal := 0 end;
|
|
end
|
|
else {double LR rotation}
|
|
begin
|
|
B2 := P^.Left^.Right^.Bal;
|
|
RotL(P^.Left);
|
|
RotR(P);
|
|
//adjust balance factors
|
|
if B2=-1 then P^.Right^.Bal := 1 else P^.Right^.Bal := 0;
|
|
if B2= 1 then P^.Left^.Bal := -1 else P^.Left^.Bal := 0;
|
|
P^.Bal := 0;
|
|
end;
|
|
end;{-1}
|
|
end{case}
|
|
end;{BalanceRight}
|
|
|
|
procedure DelRM(var R: PNode; var S: PNode; var HeightChange: Boolean);
|
|
// Make S refer to rightmost element of tree with root R;
|
|
// Remove that element from the tree
|
|
begin
|
|
if R^.Right = nil then
|
|
begin S := R; R := R^.Left; HeightChange := true end
|
|
else
|
|
begin
|
|
DelRM(R^.Right, S, HeightChange);
|
|
if HeightChange then BalanceRight(R, HeightChange)
|
|
end
|
|
end;
|
|
|
|
//---------------------------------------
|
|
//---****--- Class TAvlTree ---*****-----
|
|
//---------------------------------------
|
|
|
|
constructor TAvlTree.Create;
|
|
begin
|
|
inherited;
|
|
|
|
FRoot := nil;
|
|
end;
|
|
|
|
destructor TAvlTree.Destroy;
|
|
begin
|
|
Clear;
|
|
|
|
inherited;
|
|
end;
|
|
|
|
procedure TAvlTree.Clear;
|
|
begin
|
|
TreeDispose(FRoot);
|
|
FRoot := nil;
|
|
end;
|
|
|
|
procedure TAvlTree.DeleteNode(X: PNode);
|
|
begin
|
|
// delete handler installed?
|
|
if Assigned(FOnDelete) then
|
|
FOnDelete(Self, @X^.Data);
|
|
|
|
// dispose of memory
|
|
Dispose(X);
|
|
Dec(FCount);
|
|
end;
|
|
|
|
procedure TAvlTree.TreeDispose(X: PNode);
|
|
var
|
|
P: PNode;
|
|
begin
|
|
// nothing to dispose of?
|
|
if X = nil then
|
|
exit;
|
|
|
|
// use in-order visiting, maybe someone likes sequential ordering
|
|
TreeDispose(X^.Left);
|
|
P := X^.Right;
|
|
|
|
// free mem
|
|
DeleteNode(X);
|
|
|
|
// free right child
|
|
TreeDispose(P);
|
|
end;
|
|
|
|
function TAvlTree.Find(Key: TKeyType): TExtraData;
|
|
var
|
|
H: PNode;
|
|
begin
|
|
H := FRoot;
|
|
while (H <> nil) and (H^.Data.ID <> Key) do // use conditional and
|
|
if Key < H^.Data.ID then
|
|
H := H^.Left
|
|
else
|
|
H := H^.Right;
|
|
|
|
if H <> nil then
|
|
Result := H^.Data.ExtraData
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TAvlTree.Insert(Key: TKeyType; Extra: TExtraData);
|
|
var
|
|
H: PNode;
|
|
begin
|
|
// make new node
|
|
New(H);
|
|
with H^ do
|
|
begin
|
|
Data.ID := Key;
|
|
Data.ExtraData := Extra;
|
|
Left := nil;
|
|
Right := nil;
|
|
Bal := 0;
|
|
end;
|
|
// insert new node
|
|
InternalInsert(H, FRoot);
|
|
// check tree
|
|
// assert(CheckTree(FRoot));
|
|
end;
|
|
|
|
procedure TAvlTree.Delete(Key: TKeyType);
|
|
begin
|
|
InternalDelete(Key, FRoot);
|
|
// assert(CheckTree(FRoot));
|
|
end;
|
|
|
|
procedure TAvlTree.InternalInsert(X: PNode; var P: PNode);
|
|
begin
|
|
if P = nil
|
|
then begin P := X; Inc(FCount); FHeightChange := true end
|
|
else
|
|
if X^.Data.ID < P^.Data.ID then
|
|
begin
|
|
{ less }
|
|
InternalInsert(X, P^.Left);
|
|
if FHeightChange then {Left branch has grown higher}
|
|
case P^.Bal of
|
|
1: begin P^.Bal := 0; FHeightChange := false end;
|
|
0: begin P^.Bal := -1 end;
|
|
-1: begin {Rebalance}
|
|
if P^.Left^.Bal = -1
|
|
then {single R rotation}
|
|
begin
|
|
RotR(P);
|
|
//adjust balance factor:
|
|
P^.Right^.Bal := 0;
|
|
end
|
|
else {double LR rotation}
|
|
begin
|
|
RotL(P^.Left);
|
|
RotR(P);
|
|
//adjust balance factor:
|
|
case P^.Bal of
|
|
-1: begin P^.Left^.Bal := 0; P^.Right^.Bal := 1 end;
|
|
0: begin P^.Left^.Bal := 0; P^.Right^.Bal := 0 end;
|
|
1: begin P^.Left^.Bal := -1; P^.Right^.Bal := 0 end;
|
|
end;
|
|
end;
|
|
P^.Bal := 0;
|
|
FHeightChange := false;
|
|
// assert(CheckTree(P));
|
|
end{-1}
|
|
end{case}
|
|
end else
|
|
if X^.Data.ID > P^.Data.ID then
|
|
begin
|
|
{ greater }
|
|
InternalInsert(X, P^.Right);
|
|
if FHeightChange then {Right branch has grown higher}
|
|
case P^.Bal of
|
|
-1: begin P^.Bal := 0; FHeightChange := false end;
|
|
0: begin P^.Bal := 1 end;
|
|
1: begin {Rebalance}
|
|
if P^.Right^.Bal = 1
|
|
then {single L rotation}
|
|
begin
|
|
RotL(P);
|
|
//adjust balance factor:
|
|
P^.Left.Bal := 0;
|
|
end
|
|
else {double RL rotation}
|
|
begin
|
|
RotR(P^.Right);
|
|
RotL(P);
|
|
//adjust balance factor
|
|
case P^.Bal of
|
|
1: begin P^.Right^.Bal := 0; P^.Left^.Bal := -1 end;
|
|
0: begin P^.Right^.Bal := 0; P^.Left^.Bal := 0 end;
|
|
-1: begin P^.Right^.Bal := 1; P^.Left^.Bal := 0 end;
|
|
end;
|
|
end;
|
|
P^.Bal := 0;
|
|
FHeightChange := false;
|
|
// assert(CheckTree(P));
|
|
end{1}
|
|
end{case}
|
|
end {greater} else begin
|
|
{X already present; do not insert again}
|
|
FHeightChange := false;
|
|
end;
|
|
|
|
// assert(CheckTree(P));
|
|
end;{InternalInsert}
|
|
|
|
procedure TAvlTree.InternalDelete(X: TKeyType; var P: PNode);
|
|
var
|
|
Q: PNode;
|
|
H: TData;
|
|
begin
|
|
if P = nil then
|
|
FHeightChange := false
|
|
else
|
|
if X < P^.Data.ID then
|
|
begin
|
|
InternalDelete(X, P^.Left);
|
|
if FHeightChange then BalanceLeft(P, FHeightChange)
|
|
end else
|
|
if X > P^.Data.ID then
|
|
begin
|
|
InternalDelete(X, P^.Right);
|
|
if FHeightChange then BalanceRight(P, FHeightChange)
|
|
end else begin
|
|
if P^.Right = nil then
|
|
begin Q := P; P := P^.Left; FHeightChange := true end
|
|
else if P^.Left = nil then
|
|
begin Q := P; P := P^.Right; FHeightChange := true end
|
|
else
|
|
begin
|
|
DelRM(P^.Left, Q, FHeightChange);
|
|
H := P^.Data;
|
|
P^.Data := Q^.Data;
|
|
Q^.Data := H;
|
|
if FHeightChange then BalanceLeft(P, FHeightChange)
|
|
end;
|
|
DeleteNode(Q)
|
|
end;{eq}
|
|
end;{InternalDelete}
|
|
|
|
function TAvlTree.Lowest: PData;
|
|
var
|
|
H: PNode;
|
|
begin
|
|
H := FRoot;
|
|
if H = nil then
|
|
begin
|
|
Result := nil;
|
|
exit;
|
|
end;
|
|
|
|
while H^.Left <> nil do
|
|
H := H^.Left;
|
|
Result := @H^.Data;
|
|
end;
|
|
|
|
end.
|