1
0
mirror of synced 2024-11-14 15:03:51 +00:00
x2utils/X2UtBinaryTree.pas

1184 lines
29 KiB
ObjectPascal
Raw Normal View History

{
:: X2UtBinaryTree contains an implementation of the binary tree algorithm,
:: along with various descendants which implement support for a range of value
:: types (such as pointers, integers and strings). This effectively makes it
:: an associative array based on an integer key. For a hash implementation
:: based on string keys use the X2UtHashes unit.
::
:: P.S. I realise that a "B-Tree" is different from a binary tree. For
:: convenience reasons I will however ignore your ranting and call my
:: classes "TX2UtBTree". ;)
::
2004-08-20 10:03:59 +00:00
:: This unit contains code based on GNU libavl:
:: http://www.msu.edu/~pfaffben/avl/libavl.html/
::
:: Last changed: $Date$
:: Revision: $Rev$
:: Author: $Author$
}
unit X2UtBinaryTree;
interface
uses
2004-08-20 10:03:59 +00:00
SysUtils;
type
//:$ Raised when the cursor is invalid.
//:: Call Reset on the binary tree to create a valid cursor.
EX2BTreeInvalidCursor = class(Exception);
{
:$ Internal representation of a node.
}
PPX2BTreeNode = ^PX2BTreeNode;
PX2BTreeNode = ^TX2BTreeNode;
TX2BTreeNode = record
Index: Cardinal;
Children: array[0..1] of PX2BTreeNode;
2004-08-20 10:03:59 +00:00
Balance: Integer;
Data: record end;
end;
{
2004-08-20 10:03:59 +00:00
:$ Internal node stack
}
TX2BTreeStackItem = record
Node: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
Direction: Integer;
end;
TX2BTreeStack = class(TObject)
private
FItems: array of TX2BTreeStackItem;
FCount: Integer;
FPosition: Integer;
2004-08-20 10:03:59 +00:00
function GetCount(): Integer;
function GetNode(Index: Integer): PX2BTreeNode;
2004-08-20 10:03:59 +00:00
function GetDirection(Index: Integer): Integer;
procedure SetDirection(Index: Integer; const Value: Integer);
procedure SetNode(Index: Integer; const Value: PX2BTreeNode);
public
constructor Create();
procedure Clear();
procedure Push(const ANode: PX2BTreeNode; const ADirection: Integer = 0);
function Pop(): PX2BTreeNode; overload;
function Pop(var ADirection: Integer): PX2BTreeNode; overload;
2004-08-20 10:03:59 +00:00
property Node[Index: Integer]: PX2BTreeNode read GetNode
write SetNode; default;
property Direction[Index: Integer]: Integer read GetDirection
write SetDirection;
2004-07-23 13:15:14 +00:00
2004-08-20 10:03:59 +00:00
property Count: Integer read GetCount;
end;
{
:$ Binary tree implementation
:: This class implements a binary tree without knowing anything about
:: the data it contains.
}
TX2CustomBTree = class(TObject)
private
2004-08-20 10:03:59 +00:00
FCount: Integer;
FRoot: PX2BTreeNode;
FCursor: PX2BTreeNode;
FIsReset: Boolean;
FParents: TX2BTreeStack;
FNodeSize: Cardinal;
FDataSize: Cardinal;
function GetTotalSize(): Cardinal;
2004-07-23 13:15:14 +00:00
protected
function GetCurrentIndex(): Cardinal;
function GetNodeData(const ANode: PX2BTreeNode): Pointer; virtual;
procedure CopyNodeData(const ASource, ADest: PX2BTreeNode);
2004-08-20 10:03:59 +00:00
procedure BalanceInsert(var ANode: PX2BTreeNode);
2004-08-20 10:03:59 +00:00
function LookupNode(const AIndex: Cardinal;
const ACanCreate: Boolean = False;
const ASetCursor: Boolean = False): PX2BTreeNode;
procedure RotateLeft(var ANode: PX2BTreeNode);
procedure RotateRight(var ANode: PX2BTreeNode);
2004-08-20 10:03:59 +00:00
function DeleteLeftShrunk(var ANode: PX2BTreeNode): Integer;
function DeleteRightShrunk(var ANode: PX2BTreeNode): Integer;
function DeleteFindHighest(const ATarget: PX2BTreeNode;
var ANode: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
out AResult: Integer): Boolean;
function DeleteFindLowest(const ATarget: PX2BTreeNode;
var ANode: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
out AResult: Integer): Boolean;
function InternalDeleteNode(var ARoot: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
const AIndex: Cardinal): Integer;
procedure DeleteNode(const AIndex: Cardinal);
procedure InitNode(var ANode: PX2BTreeNode); virtual;
procedure FreeNode(var ANode: PX2BTreeNode); virtual;
procedure ClearCursor(); virtual;
function ValidCursor(const ARaiseError: Boolean = True): Boolean; virtual;
property Cursor: PX2BTreeNode read FCursor write FCursor;
property Root: PX2BTreeNode read FRoot;
property IsReset: Boolean read FIsReset write FIsReset;
property Parents: TX2BTreeStack read FParents;
2004-07-23 13:15:14 +00:00
property NodeSize: Cardinal read FNodeSize;
property TotalSize: Cardinal read GetTotalSize;
// Note: do NOT change DataSize after the first node has
// been created! This will result in an Access Violation!
property DataSize: Cardinal read FDataSize write FDataSize;
2004-07-23 13:15:14 +00:00
//:$ Returns the index at the current cursor location.
property CurrentIndex: Cardinal read GetCurrentIndex;
public
constructor Create(); virtual;
destructor Destroy(); override;
2004-07-23 13:15:14 +00:00
//:$ Clears the tree.
procedure Clear();
2004-07-23 13:15:14 +00:00
//:$ Deletes a node from the tree.
procedure Delete(const AIndex: Cardinal);
2004-07-23 13:15:14 +00:00
//:$ Checks if an index exists in the tree.
//:: If the ASetCursor parameter is set to True, the cursor will be
//:: positioned at the item if it is found.
function Exists(const AIndex: Cardinal; const ASetCursor: Boolean = False): Boolean;
//:$ Resets the node cursor.
//:: The node cursor can be used to traverse through the binary tree.
//:: Call Reset first, followed by Next to get the first item. You can
//:: continue to call Next until it returns false. The CurrentIndex and
//:: CurrentValue properties will only be valid within the traversal.
//:! Adding or removing items will result in a loss of the current cursor
//:! until the next Reset call.
procedure Reset(); virtual;
2004-07-23 13:15:14 +00:00
//:$ Moves the node cursor to the next node.
//:! The order in which nodes are traversed is from top to bottom, left
//:! to right. Do not depend on the binary tree to sort the output.
function Next(): Boolean; virtual;
2004-08-20 10:03:59 +00:00
//:$ Contains the number of nodes in the tree
property Count: Integer read FCount;
2004-07-23 13:15:14 +00:00
end;
2004-07-23 13:15:14 +00:00
{
:$ Binary tree implementation for pointer values
2004-07-23 13:15:14 +00:00
}
TX2BTree = class(TX2CustomBTree)
private
function GetItem(Index: Cardinal): Pointer;
procedure SetItem(Index: Cardinal; const Value: Pointer);
function GetCurrentValue(): Pointer;
2004-07-23 13:15:14 +00:00
public
constructor Create(); override;
2004-07-23 13:15:14 +00:00
property CurrentIndex;
//:$ Gets or sets an item.
property Items[Index: Cardinal]: Pointer read GetItem
write SetItem; default;
//:$ Returns the value at the current cursor location
property CurrentValue: Pointer read GetCurrentValue;
end;
{
:$ Binary tree implementation for integer values
}
TX2IntegerBTree = class(TX2BTree)
2004-07-23 13:15:14 +00:00
protected
function GetItem(Index: Cardinal): Integer;
procedure SetItem(Index: Cardinal; const Value: Integer);
2004-08-20 11:18:01 +00:00
function GetCurrentValue(): Integer;
public
//:$ Gets or sets an item.
property Items[Index: Cardinal]: Integer read GetItem
write SetItem; default;
//:$ Returns the value at the current cursor location
property CurrentValue: Integer read GetCurrentValue;
end;
{
:$ Binary tree implementation for string values
}
TX2StringBTree = class(TX2CustomBTree)
2004-07-23 13:15:14 +00:00
protected
function GetItem(Index: Cardinal): String;
procedure SetItem(Index: Cardinal; const Value: String);
function GetCurrentValue(): String;
protected
procedure InitNode(var ANode: PX2BTreeNode); override;
procedure FreeNode(var ANode: PX2BTreeNode); override;
public
constructor Create(); override;
property CurrentIndex;
//:$ Gets or sets an item.
property Items[Index: Cardinal]: String read GetItem
write SetItem; default;
//:$ Returns the value at the current cursor location
property CurrentValue: String read GetCurrentValue;
end;
{
:$ Binary tree implementation for object values
}
TX2ObjectBTree = class(TX2BTree)
private
FOwnsObjects: Boolean;
protected
function GetItem(Index: Cardinal): TObject;
procedure SetItem(Index: Cardinal; const Value: TObject);
function GetCurrentValue(): TObject;
protected
procedure FreeNode(var ANode: PX2BTreeNode); override;
public
constructor Create(); overload; override;
constructor Create(AOwnsObjects: Boolean); reintroduce; overload;
//:$ Gets or sets an item.
property Items[Index: Cardinal]: TObject read GetItem
write SetItem; default;
//:$ Returns the value at the current cursor location
property CurrentValue: TObject read GetCurrentValue;
//:$ Determines if objects are destroyed when they are removed
property OwnsObjects: Boolean read FOwnsObjects
write FOwnsObjects;
end;
2004-07-29 13:48:35 +00:00
resourcestring
RSInvalidCursor = 'Cursor is invalid!';
RSInvalidDataSize = 'Invalid data size!';
implementation
resourcestring
RSOrphanNode = 'BUG: Node does not seem to belong to it''s parent!';
RSTooManyPops = 'More Pops than Pushes!';
const
CStackSize = 32;
2004-08-20 10:03:59 +00:00
CLeft = 0;
CRight = 1;
CError = 0;
COK = 1;
CBalance = 2;
{========================== TX2BTreeStack
Item Management
========================================}
constructor TX2BTreeStack.Create;
begin
inherited;
FCount := CStackSize;
FPosition := -1;
SetLength(FItems, FCount);
end;
procedure TX2BTreeStack.Clear;
begin
FCount := CStackSize;
FPosition := -1;
SetLength(FItems, FCount);
end;
procedure TX2BTreeStack.Push;
begin
Inc(FPosition);
if FPosition >= FCount then
begin
Inc(FCount, FCount);
SetLength(FItems, FCount);
end;
2004-08-20 10:03:59 +00:00
with FItems[FPosition] do
begin
Node := ANode;
Direction := ADirection;
end;
end;
function TX2BTreeStack.Pop(): PX2BTreeNode;
begin
Result := nil;
2004-08-20 10:03:59 +00:00
if FPosition >= 0 then
begin
2004-08-20 10:03:59 +00:00
Result := FItems[FPosition].Node;
Dec(FPosition);
end;
end;
function TX2BTreeStack.Pop(var ADirection: Integer): PX2BTreeNode;
2004-08-20 10:03:59 +00:00
begin
Result := nil;
if FPosition >= 0 then
begin
ADirection := FItems[FPosition].Direction;
Result := FItems[FPosition].Node;
Dec(FPosition);
end;
end;
function TX2BTreeStack.GetNode;
2004-08-20 10:03:59 +00:00
begin
Assert((Index >= 0) and (Index <= FPosition), '* BUG * Invalid stack index!');
Result := FItems[Index].Node;
end;
2004-07-23 13:15:14 +00:00
procedure TX2BTreeStack.SetNode;
2004-07-23 13:15:14 +00:00
begin
2004-08-20 10:03:59 +00:00
Assert((Index >= 0) and (Index <= FPosition), '* BUG * Invalid stack index!');
FItems[Index].Node := Value;
end;
2004-07-23 13:15:14 +00:00
function TX2BTreeStack.GetDirection;
2004-08-20 10:03:59 +00:00
begin
Assert((Index >= 0) and (Index <= FPosition), '* BUG * Invalid stack index!');
Result := FItems[Index].Direction;
end;
procedure TX2BTreeStack.SetDirection;
2004-08-20 10:03:59 +00:00
begin
Assert((Index >= 0) and (Index <= FPosition), '* BUG * Invalid stack index!');
FItems[Index].Direction := Value;
end;
function TX2BTreeStack.GetCount;
2004-08-20 10:03:59 +00:00
begin
Result := FPosition + 1;
2004-07-23 13:15:14 +00:00
end;
{======================= TX2CustomBTree
Initialization
========================================}
constructor TX2CustomBTree.Create;
begin
inherited;
FParents := TX2BTreeStack.Create();
FNodeSize := SizeOf(TX2BTreeNode);
end;
destructor TX2CustomBTree.Destroy;
begin
2004-08-20 10:03:59 +00:00
FreeAndNil(FParents);
Clear();
inherited;
end;
{========================= TX2CustomBTree
Tree Management
========================================}
function TX2CustomBTree.GetNodeData;
begin
Assert(DataSize > 0, RSInvalidDataSize);
Result := Pointer(Cardinal(ANode) + NodeSize);
end;
procedure TX2CustomBTree.CopyNodeData;
2004-08-20 10:03:59 +00:00
begin
ADest^.Index := ASource^.Index;
Move(GetNodeData(ASource)^,
GetNodeData(ADest)^,
DataSize);
end;
procedure TX2CustomBTree.BalanceInsert;
var
pNode: PX2BTreeNode;
pSwap: PX2BTreeNode;
begin
2004-08-20 10:03:59 +00:00
if ANode^.Balance = -2 then
begin
2004-08-20 10:03:59 +00:00
// Left-heavy
pNode := ANode^.Children[CLeft];
2004-08-20 10:03:59 +00:00
if pNode^.Balance = -1 then
begin
pSwap := pNode;
ANode^.Children[CLeft] := pNode^.Children[CRight];
pNode^.Children[CRight] := ANode;
pNode^.Balance := 0;
ANode^.Balance := 0;
end else
begin
Assert(pNode^.Balance = 1, '* BUG * Unexpected node balance');
pSwap := pNode^.Children[CRight];
pNode^.Children[CRight] := pSwap^.Children[CLeft];
pSwap^.Children[CLeft] := pNode;
ANode^.Children[CLeft] := pSwap^.Children[CRight];
pSwap^.Children[CRight] := ANode;
case pSwap^.Balance of
-1:
begin
pNode^.Balance := 0;
ANode^.Balance := 1;
end;
0:
begin
pNode^.Balance := 0;
ANode^.Balance := 0;
end;
else
pNode^.Balance := -1;
ANode^.Balance := 0;
end;
2004-07-23 13:15:14 +00:00
2004-08-20 10:03:59 +00:00
pSwap^.Balance := 0;
end;
2004-08-20 10:03:59 +00:00
ANode := pSwap;
end else if ANode^.Balance = 2 then
begin
2004-08-20 10:03:59 +00:00
// Right-heavy
pNode := ANode^.Children[CRight];
if pNode^.Balance = 1 then
begin
2004-08-20 10:03:59 +00:00
pSwap := pNode;
ANode^.Children[CRight] := pNode^.Children[CLeft];
pNode^.Children[CLeft] := ANode;
pNode^.Balance := 0;
ANode^.Balance := 0;
end else
begin
2004-08-20 10:03:59 +00:00
Assert(pNode^.Balance = -1, '* BUG * Unexpected node balance');
pSwap := pNode^.Children[CLeft];
pNode^.Children[CLeft] := pSwap^.Children[CRight];
pSwap^.Children[CRight] := pNode;
ANode^.Children[CRight] := pSwap^.Children[CLeft];
pSwap^.Children[CLeft] := ANode;
case pSwap^.Balance of
1:
begin
pNode^.Balance := 0;
ANode^.Balance := -1;
end;
0:
begin
pNode^.Balance := 0;
ANode^.Balance := 0;
end;
else
2004-08-20 10:03:59 +00:00
pNode^.Balance := 1;
ANode^.Balance := 0;
end;
pSwap^.Balance := 0;
end;
ANode := pSwap;
end;
end;
function TX2CustomBTree.LookupNode;
2004-08-20 10:03:59 +00:00
var
pCurrent: PPX2BTreeNode;
pBalance: PPX2BTreeNode;
pLast: PX2BTreeNode;
pNode: PX2BTreeNode;
pPath: TX2BTreeStack;
2004-08-20 10:03:59 +00:00
begin
Result := nil;
if ASetCursor then
Parents.Clear();
pPath := TX2BTreeStack.Create();
2004-08-20 10:03:59 +00:00
try
pCurrent := @FRoot;
pBalance := nil;
repeat
if Assigned(pCurrent^) then
begin
2004-08-20 10:03:59 +00:00
pPath.Push(pCurrent^);
if pCurrent^^.Balance <> 0 then
pBalance := pCurrent;
if AIndex > pCurrent^^.Index then
// Continue on the right side
pCurrent := @pCurrent^^.Children[CRight]
else if AIndex < pCurrent^^.Index then
// Continue on the left side
pCurrent := @pCurrent^^.Children[CLeft]
else
begin
2004-08-20 10:03:59 +00:00
// Found it!
Result := pCurrent^;
break;
end;
2004-08-20 10:03:59 +00:00
end else if ACanCreate then
begin
// Create new node
InitNode(pCurrent^);
pCurrent^^.Index := AIndex;
2004-08-20 10:03:59 +00:00
// Update balance factors
pLast := pCurrent^;
pNode := pPath.Pop();
while Assigned(pNode) do
begin
if pNode^.Children[CLeft] = pLast then
Dec(pNode^.Balance)
else
Inc(pNode^.Balance);
if Assigned(pBalance) and (pNode = pBalance^) then
break;
pLast := pNode;
pNode := pPath.Pop();
end;
if Assigned(pBalance) then
BalanceInsert(pBalance^);
Result := pCurrent^;
2004-08-20 10:03:59 +00:00
break;
end else
break;
2004-08-20 10:03:59 +00:00
until False;
finally
FreeAndNil(pPath);
end;
end;
procedure TX2CustomBTree.RotateLeft;
2004-08-20 10:03:59 +00:00
var
pSwap: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
begin
pSwap := ANode;
ANode := ANode^.Children[CRight];
pSwap^.Children[CRight] := ANode^.Children[CLeft];
ANode^.Children[CLeft] := pSwap;
end;
procedure TX2CustomBTree.RotateRight;
2004-08-20 10:03:59 +00:00
var
pSwap: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
begin
pSwap := ANode;
ANode := ANode^.Children[CLeft];
pSwap^.Children[CLeft] := ANode^.Children[CRight];
ANode^.Children[CRight] := pSwap;
end;
function TX2CustomBTree.DeleteLeftShrunk;
2004-08-20 10:03:59 +00:00
begin
2004-08-20 11:18:01 +00:00
Result := CError;
2004-08-20 10:03:59 +00:00
case ANode^.Balance of
-1:
begin
ANode^.Balance := 0;
Result := CBalance;
end;
2004-08-20 10:03:59 +00:00
0:
begin
2004-08-20 10:03:59 +00:00
ANode^.Balance := 1;
Result := COK;
end;
1:
begin
case ANode^.Children[CRight]^.Balance of
1:
begin
if ANode^.Children[CRight]^.Balance = 0 then
ANode^.Balance := 1
else
ANode^.Balance := 0;
RotateLeft(ANode);
Result := CBalance;
end;
0:
begin
ANode^.Balance := 1;
ANode^.Children[CRight]^.Balance := -1;
RotateLeft(ANode);
Result := COK;
end;
-1:
begin
case ANode^.Children[CRight]^.Children[CLeft]^.Balance of
-1:
begin
ANode^.Balance := 0;
ANode^.Children[CRight]^.Balance := 1;
end;
0:
begin
ANode^.Balance := 0;
ANode^.Children[CRight]^.Balance := 0;
end;
1:
begin
ANode^.Balance := -1;
ANode^.Children[CRight]^.Balance := 0;
end;
end;
ANode^.Children[CRight]^.Children[CLeft]^.Balance := 0;
RotateRight(ANode^.Children[CRight]);
RotateLeft(ANode);
Result := CBalance;
end;
end;
2004-08-20 10:03:59 +00:00
end;
end;
end;
function TX2CustomBTree.DeleteRightShrunk;
2004-08-20 10:03:59 +00:00
begin
2004-08-20 11:18:01 +00:00
Result := CError;
2004-08-20 10:03:59 +00:00
case ANode^.Balance of
1:
begin
ANode^.Balance := 0;
Result := CBalance;
end;
0:
begin
ANode^.Balance := -1;
Result := COK;
end;
-1:
begin
case ANode^.Children[CLeft]^.Balance of
-1:
begin
if ANode^.Children[CLeft]^.Balance = 0 then
ANode^.Balance := 1
else
ANode^.Balance := 0;
RotateRight(ANode);
Result := CBalance;
end;
0:
begin
ANode^.Balance := -1;
ANode^.Children[CLeft]^.Balance := 1;
RotateRight(ANode);
Result := COK;
end;
1:
begin
case ANode^.Children[CLeft]^.Children[CRight]^.Balance of
-1:
begin
ANode^.Balance := 1;
ANode^.Children[CLeft]^.Balance := 0;
end;
0:
begin
ANode^.Balance := 0;
ANode^.Children[CLeft]^.Balance := 0;
end;
1:
begin
ANode^.Balance := 1;
ANode^.Children[CLeft]^.Balance := 0;
end;
end;
ANode^.Children[CLeft]^.Children[CRight]^.Balance := 0;
RotateLeft(ANode^.Children[CLeft]);
RotateRight(ANode);
Result := CBalance;
end;
end;
end;
2004-08-20 10:03:59 +00:00
end;
end;
function TX2CustomBTree.DeleteFindHighest;
2004-08-20 10:03:59 +00:00
var
pSwap: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
begin
AResult := CBalance;
Result := False;
if not Assigned(ANode) then
exit;
if Assigned(ANode^.Children[CRight]) then
begin
if not DeleteFindHighest(ATarget, ANode^.Children[CRight], AResult) then
begin
Result := False;
exit;
end;
2004-08-20 10:03:59 +00:00
if AResult = CBalance then
AResult := DeleteRightShrunk(ANode);
Result := True;
exit;
end;
2004-07-23 13:15:14 +00:00
2004-08-20 10:03:59 +00:00
pSwap := ANode;
CopyNodeData(ANode, ATarget);
ANode := ANode^.Children[CLeft];
FreeNode(pSwap);
Result := True;
end;
function TX2CustomBTree.DeleteFindLowest;
2004-08-20 10:03:59 +00:00
var
pSwap: PX2BTreeNode;
2004-08-20 10:03:59 +00:00
begin
AResult := CBalance;
Result := False;
if not Assigned(ANode) then
exit;
if Assigned(ANode^.Children[CLeft]) then
2004-07-23 13:15:14 +00:00
begin
2004-08-20 10:03:59 +00:00
if not DeleteFindLowest(ATarget, ANode^.Children[CLeft], AResult) then
2004-07-23 13:15:14 +00:00
begin
2004-08-20 10:03:59 +00:00
Result := False;
exit;
2004-07-23 13:15:14 +00:00
end;
2004-08-20 10:03:59 +00:00
if AResult = CBalance then
AResult := DeleteLeftShrunk(ANode);
Result := True;
exit;
2004-07-23 13:15:14 +00:00
end;
2004-08-20 10:03:59 +00:00
pSwap := ANode;
CopyNodeData(ANode, ATarget);
ANode := ANode^.Children[CRight];
FreeNode(pSwap);
Result := True;
end;
function TX2CustomBTree.InternalDeleteNode;
2004-08-20 10:03:59 +00:00
var
iResult: Integer;
begin
if AIndex < ARoot^.Index then
begin
// Continue on the left side
iResult := InternalDeleteNode(ARoot^.Children[CLeft], AIndex);
if iResult = CBalance then
begin
Result := DeleteLeftShrunk(ARoot);
exit;
end;
Result := iResult;
exit;
end;
if AIndex > ARoot^.Index then
begin
// Continue on the right side
iResult := InternalDeleteNode(ARoot^.Children[CRight], AIndex);
if iResult = CBalance then
begin
Result := DeleteRightShrunk(ARoot);
exit;
end;
Result := iResult;
exit;
end;
if Assigned(ARoot^.Children[CLeft]) then
if DeleteFindHighest(ARoot, ARoot^.Children[CLeft], iResult) then
begin
if iResult = CBalance then
iResult := DeleteLeftShrunk(ARoot);
Result := iResult;
exit;
end;
if Assigned(ARoot^.Children[CRight]) then
if DeleteFindLowest(ARoot, ARoot^.Children[CRight], iResult) then
begin
if iResult = CBalance then
iResult := DeleteRightShrunk(ARoot);
Result := iResult;
exit;
end;
FreeNode(ARoot);
Result := CBalance;
end;
procedure TX2CustomBTree.DeleteNode;
2004-08-20 10:03:59 +00:00
begin
if not Assigned(FRoot) then
exit;
InternalDeleteNode(FRoot, AIndex);
end;
procedure TX2CustomBTree.InitNode;
begin
Assert(DataSize > 0, RSInvalidDataSize);
GetMem(ANode, TotalSize);
FillChar(ANode^, TotalSize, #0);
2004-08-20 10:03:59 +00:00
Inc(FCount);
ClearCursor();
end;
procedure TX2CustomBTree.FreeNode;
begin
FreeMem(ANode, TotalSize);
ANode := nil;
2004-08-20 10:03:59 +00:00
Dec(FCount);
ClearCursor();
end;
procedure TX2CustomBTree.Clear;
procedure ClearNode(var ANode: PX2BTreeNode);
2004-08-20 10:03:59 +00:00
begin
if Assigned(ANode^.Children[CLeft]) then
ClearNode(ANode^.Children[CLeft]);
if Assigned(ANode^.Children[CRight]) then
ClearNode(ANode^.Children[CRight]);
FreeNode(ANode);
end;
begin
if Assigned(FRoot) then
2004-08-20 10:03:59 +00:00
ClearNode(FRoot);
FRoot := nil;
end;
procedure TX2CustomBTree.Delete;
begin
2004-08-20 10:03:59 +00:00
DeleteNode(AIndex);
end;
function TX2CustomBTree.Exists;
2004-07-23 13:15:14 +00:00
begin
Result := Assigned(LookupNode(AIndex, False, True));
end;
2004-07-23 13:15:14 +00:00
{========================= TX2CustomBTree
Tree Traversing
========================================}
function TX2CustomBTree.ValidCursor;
begin
Result := (Assigned(Cursor) and (not IsReset));
if (not Result) and (ARaiseError) then
raise EX2BTreeInvalidCursor.Create(RSInvalidCursor);
end;
procedure TX2CustomBTree.ClearCursor;
begin
Cursor := nil;
end;
procedure TX2CustomBTree.Reset;
begin
Cursor := Root;
IsReset := True;
end;
function TX2CustomBTree.Next;
var
pParent: PX2BTreeNode;
pCurrent: PX2BTreeNode;
begin
Result := False;
if not Assigned(Cursor) then
begin
IsReset := False;
exit;
end;
if not IsReset then
begin
2004-08-20 10:03:59 +00:00
if Assigned(Cursor^.Children[CLeft]) then
begin
// Valid left path, follow it
2004-08-20 10:03:59 +00:00
Parents.Push(Cursor);
Cursor := Cursor^.Children[CLeft];
Result := True;
2004-08-20 10:03:59 +00:00
end else if Assigned(Cursor^.Children[CRight]) then
begin
// Valid right path, follow it
2004-08-20 10:03:59 +00:00
Parents.Push(Cursor);
Cursor := Cursor^.Children[CRight];
Result := True;
end else
begin
// Neither is valid, traverse back up the parent stack until
// a node if found with a sibling
pCurrent := Cursor;
2004-08-20 10:03:59 +00:00
pParent := Parents.Pop();
ClearCursor();
while Assigned(pParent) do
begin
2004-08-20 10:03:59 +00:00
if Assigned(pParent^.Children[CRight]) and
(pParent^.Children[CRight] <> pCurrent) then
begin
// Parent has a sibling, follow it
2004-08-20 10:03:59 +00:00
Parents.Push(pParent);
Cursor := pParent^.Children[CRight];
Result := True;
break;
end;
pCurrent := pParent;
2004-08-20 10:03:59 +00:00
pParent := Parents.Pop();
end;
end;
end else
begin
IsReset := False;
Result := True;
end;
end;
function TX2CustomBTree.GetCurrentIndex;
begin
Result := 0;
if ValidCursor(True) then
Result := Cursor^.Index;
end;
function TX2CustomBTree.GetTotalSize;
begin
Result := FNodeSize + FDataSize;
end;
{=============================== TX2BTree
Item Management
========================================}
constructor TX2BTree.Create;
begin
inherited;
DataSize := SizeOf(Pointer);
end;
function TX2BTree.GetItem;
var
pNode: PX2BTreeNode;
begin
Result := nil;
pNode := LookupNode(Index);
if Assigned(pNode) then
Result := PPointer(GetNodeData(pNode))^;
end;
procedure TX2BTree.SetItem;
var
pNode: PX2BTreeNode;
begin
pNode := LookupNode(Index, True);
if Assigned(pNode) then
PPointer(GetNodeData(pNode))^ := Value;
end;
function TX2BTree.GetCurrentValue;
begin
Result := nil;
if ValidCursor(True) then
Result := PPointer(GetNodeData(Cursor))^;
end;
{======================== TX2IntegerBTree
Item Management
========================================}
function TX2IntegerBTree.GetItem;
begin
Result := Integer(inherited GetItem(Index));
end;
procedure TX2IntegerBTree.SetItem;
begin
inherited SetItem(Index, Pointer(Value));
end;
function TX2IntegerBTree.GetCurrentValue;
begin
Result := Integer(inherited GetCurrentValue());
end;
{========================= TX2StringBTree
Item Management
========================================}
constructor TX2StringBTree.Create;
begin
inherited;
DataSize := SizeOf(PString);
end;
procedure TX2StringBTree.InitNode;
var
pData: PString;
begin
inherited;
pData := GetNodeData(ANode);
Initialize(pData^);
end;
procedure TX2StringBTree.FreeNode;
var
pData: PString;
begin
pData := GetNodeData(ANode);
Finalize(pData^);
inherited;
end;
function TX2StringBTree.GetItem;
var
pNode: PX2BTreeNode;
begin
Result := '';
pNode := LookupNode(Index);
if Assigned(pNode) then
Result := PString(GetNodeData(pNode))^;
end;
procedure TX2StringBTree.SetItem;
var
pNode: PX2BTreeNode;
begin
pNode := LookupNode(Index, True);
if Assigned(pNode) then
PString(GetNodeData(pNode))^ := Value;
end;
function TX2StringBTree.GetCurrentValue;
begin
if ValidCursor(True) then
Result := PString(GetNodeData(Cursor))^;
end;
{========================= TX2ObjectBTree
Item Management
========================================}
constructor TX2ObjectBTree.Create;
begin
inherited;
FOwnsObjects := False;
end;
constructor TX2ObjectBTree.Create(AOwnsObjects: Boolean);
begin
inherited Create();
FOwnsObjects := AOwnsObjects;
end;
function TX2ObjectBTree.GetItem;
begin
Result := TObject(inherited GetItem(Index));
end;
procedure TX2ObjectBTree.SetItem;
begin
inherited SetItem(Index, Pointer(Value));
end;
function TX2ObjectBTree.GetCurrentValue;
begin
Result := TObject(inherited GetCurrentValue());
end;
procedure TX2ObjectBTree.FreeNode;
var
pObject: ^TObject;
begin
if FOwnsObjects then
begin
pObject := GetNodeData(ANode);
if Assigned(pObject) then
FreeAndNil(pObject^);
end;
inherited;
end;
end.