fpc/fcl/db/dbase/dbf_avl.pas
marco 46ff92bb60 * 64-bit patches from Neli and Andrew
git-svn-id: trunk@2315 -
2006-01-20 22:38:09 +00:00

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.