2004-12-28 15:43:27 +01:00
|
|
|
{** Various tree implementations.
|
|
|
|
*
|
|
|
|
* Last changed: $Date$ <br />
|
|
|
|
* Revision: $Rev$ <br />
|
|
|
|
* Author: $Author$ <br />
|
|
|
|
}
|
|
|
|
unit X2UtTrees;
|
|
|
|
|
|
|
|
interface
|
|
|
|
uses
|
|
|
|
SysUtils;
|
|
|
|
|
|
|
|
type
|
|
|
|
EBTKeyExists = class(Exception);
|
|
|
|
EBTKeyNotFound = class(Exception);
|
|
|
|
EBTCursorEof = class(Exception);
|
2005-01-12 15:47:32 +01:00
|
|
|
EBTNoCursor = class(Exception);
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
{** Internal representation of a binary tree node.
|
|
|
|
*
|
|
|
|
* For the sake of easy lookups and cleaner code I chose to let nodes know
|
|
|
|
* who their parent is. It costs 4 bytes... but that's only 4 megabytes
|
|
|
|
* overhead for each million nodes, not much of a burden nowadays.
|
|
|
|
*}
|
2005-01-12 14:06:05 +01:00
|
|
|
TX2BTLinks = array[0..11] of Byte;
|
2004-12-28 15:43:27 +01:00
|
|
|
PX2BTNode = ^TX2BTNode;
|
|
|
|
TX2BTNode = record
|
|
|
|
Key: Cardinal;
|
2005-01-12 14:06:05 +01:00
|
|
|
|
|
|
|
case Boolean of
|
|
|
|
True:
|
|
|
|
(
|
|
|
|
Parent: PX2BTNode;
|
|
|
|
Left: PX2BTNode;
|
|
|
|
Right: PX2BTNode;
|
|
|
|
);
|
|
|
|
False:
|
|
|
|
(
|
|
|
|
Links: TX2BTLinks;
|
|
|
|
);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{** Abstract cursor.
|
|
|
|
*
|
|
|
|
* Trees implement a descendant to traverse through the tree.
|
|
|
|
*}
|
|
|
|
TX2BTCustomCursor = class(TObject)
|
|
|
|
protected
|
2005-01-12 15:47:32 +01:00
|
|
|
function GetCurrentKey(): Cardinal; virtual; abstract;
|
2004-12-28 15:43:27 +01:00
|
|
|
function GetEof(): Boolean; virtual; abstract;
|
|
|
|
public
|
|
|
|
procedure First(); virtual; abstract;
|
|
|
|
procedure Next(); virtual; abstract;
|
|
|
|
|
|
|
|
property Eof: Boolean read GetEof;
|
2005-01-12 15:47:32 +01:00
|
|
|
property CurrentKey: Cardinal read GetCurrentKey;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
TX2BTCursorClass = class of TX2BTCustomCursor;
|
|
|
|
|
|
|
|
|
|
|
|
{** Default tree cursor.
|
|
|
|
*
|
|
|
|
* The default cursor traverses through the tree from top to bottom, left
|
|
|
|
* to right.
|
|
|
|
*}
|
|
|
|
TX2BTDefaultCursor = class(TX2BTCustomCursor)
|
|
|
|
private
|
2005-01-12 15:47:32 +01:00
|
|
|
FRoot: PX2BTNode;
|
2005-01-12 14:06:05 +01:00
|
|
|
FNode: PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
protected
|
2005-01-12 15:47:32 +01:00
|
|
|
function GetCurrentNode(): PX2BTNode;
|
|
|
|
function GetCurrentKey(): Cardinal; override;
|
2004-12-28 15:43:27 +01:00
|
|
|
function GetEof(): Boolean; override;
|
|
|
|
public
|
2005-01-12 15:47:32 +01:00
|
|
|
constructor Create(const ARoot: PX2BTNode); virtual;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
procedure First(); override;
|
|
|
|
procedure Next(); override;
|
2005-01-12 15:47:32 +01:00
|
|
|
|
|
|
|
property CurrentNode: PX2BTNode read GetCurrentNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{** Abstract tree manager.
|
2004-12-28 15:43:27 +01:00
|
|
|
*
|
2005-01-12 15:47:32 +01:00
|
|
|
* Trees implement a descendant to manage the tree nodes. This is where the
|
|
|
|
* actual tree is stored, and possibly optimized. All tree managers are
|
|
|
|
* assumed to store a 32-bit unsigned integer key with optional data.
|
2004-12-28 15:43:27 +01:00
|
|
|
*}
|
2005-01-12 15:47:32 +01:00
|
|
|
TX2BTCustomManager = class(TObject)
|
2004-12-28 15:43:27 +01:00
|
|
|
private
|
|
|
|
FCursor: TX2BTCustomCursor;
|
2005-01-12 19:56:31 +01:00
|
|
|
FDataSize: Cardinal;
|
2004-12-28 15:43:27 +01:00
|
|
|
protected
|
2005-01-12 15:47:32 +01:00
|
|
|
function GetCurrentKey(): Cardinal; virtual;
|
|
|
|
function GetEof(): Boolean; virtual;
|
|
|
|
|
|
|
|
procedure CursorNeeded(); virtual; abstract;
|
|
|
|
procedure InvalidateCursor(); virtual; abstract;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
property Cursor: TX2BTCustomCursor read FCursor write FCursor;
|
|
|
|
public
|
|
|
|
constructor Create(); virtual;
|
|
|
|
destructor Destroy(); override;
|
|
|
|
|
|
|
|
procedure Clear(); virtual; abstract;
|
2005-01-12 19:56:31 +01:00
|
|
|
function Insert(const AKey: Cardinal): Boolean; virtual; abstract;
|
|
|
|
function Delete(const AKey: Cardinal): Boolean; virtual; abstract;
|
2005-01-12 15:47:32 +01:00
|
|
|
|
|
|
|
function Exists(const AKey: Cardinal): Boolean; virtual; abstract;
|
|
|
|
function GetData(const AKey: Cardinal): Pointer; virtual; abstract;
|
|
|
|
|
|
|
|
procedure First(); virtual;
|
|
|
|
procedure Next(); virtual;
|
|
|
|
|
|
|
|
property CurrentKey: Cardinal read GetCurrentKey;
|
2005-01-12 19:56:31 +01:00
|
|
|
property DataSize: Cardinal read FDataSize write FDataSize;
|
2005-01-12 15:47:32 +01:00
|
|
|
property Eof: Boolean read GetEof;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TX2BTManagerClass = class of TX2BTCustomManager;
|
|
|
|
|
|
|
|
|
|
|
|
{** Default tree manager.
|
|
|
|
*}
|
|
|
|
TX2BTDefaultManager = class(TX2BTCustomManager)
|
|
|
|
private
|
|
|
|
FLastNode: PX2BTNode;
|
2005-01-12 19:56:31 +01:00
|
|
|
FRoot: PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
protected
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure CursorNeeded(); override;
|
|
|
|
procedure InvalidateCursor(); override;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
function FindLowestNode(const ANode: PX2BTNode): PX2BTNode;
|
|
|
|
function FindHighestNode(const ANode: PX2BTNode): PX2BTNode;
|
|
|
|
function FindNode(const AKey: Cardinal; out AParent: PX2BTNode): PX2BTNode;
|
|
|
|
function FindNodeOnly(const AKey: Cardinal): PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 12:08:24 +01:00
|
|
|
function LeftChild(const ANode: PX2BTNode): Boolean;
|
|
|
|
function RightChild(const ANode: PX2BTNode): Boolean;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
procedure SwapNodes(const ANode1, ANode2: PX2BTNode);
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure DeleteCleanNode(var ANode: PX2BTNode); virtual;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
procedure AllocateNode(var ANode: PX2BTNode); virtual;
|
|
|
|
procedure DeallocateNode(var ANode: PX2BTNode); virtual;
|
2005-01-12 19:56:31 +01:00
|
|
|
|
|
|
|
function GetNodeSize(): Cardinal; virtual;
|
2005-01-12 15:47:32 +01:00
|
|
|
public
|
|
|
|
procedure Clear(); override;
|
2005-01-12 19:56:31 +01:00
|
|
|
function Insert(const AKey: Cardinal): Boolean; override;
|
|
|
|
function Delete(const AKey: Cardinal): Boolean; override;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function Exists(const AKey: Cardinal): Boolean; override;
|
|
|
|
function GetData(const AKey: Cardinal): Pointer; override;
|
|
|
|
end;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{** Binary Tree implementation.
|
|
|
|
*
|
|
|
|
* Exposes the tree manager and handles node data.
|
|
|
|
*}
|
|
|
|
TX2BinaryTree = class(TObject)
|
|
|
|
private
|
|
|
|
FManager: TX2BTCustomManager;
|
|
|
|
|
|
|
|
function GetCurrentKey(): Cardinal;
|
|
|
|
function GetEof(): Boolean;
|
|
|
|
protected
|
|
|
|
function GetManagerClass(): TX2BTManagerClass; virtual;
|
2004-12-28 15:43:27 +01:00
|
|
|
public
|
|
|
|
constructor Create(); virtual;
|
|
|
|
destructor Destroy(); override;
|
|
|
|
|
|
|
|
{** Removes all nodes from the tree.
|
|
|
|
*}
|
|
|
|
procedure Clear();
|
|
|
|
|
|
|
|
{** Checks if a key already exists within the tree.
|
|
|
|
*
|
|
|
|
* @param AKey the key to search for
|
|
|
|
* @result True if the key exists, False otherwise
|
|
|
|
*}
|
|
|
|
function Exists(const AKey: Cardinal): Boolean;
|
|
|
|
|
|
|
|
{** Inserts a key into the tree.
|
|
|
|
*
|
|
|
|
* If a key already exists, an exception is raised.
|
|
|
|
*
|
|
|
|
* @param AKey the key for the new node
|
|
|
|
*}
|
|
|
|
procedure Insert(const AKey: Cardinal);
|
|
|
|
|
|
|
|
{** Deletes a key from the tree.
|
|
|
|
*
|
|
|
|
* If the key could not be found, an exception is raised.
|
|
|
|
*
|
|
|
|
* @param AKey the key to delete
|
|
|
|
*}
|
|
|
|
procedure Delete(const AKey: Cardinal);
|
|
|
|
|
|
|
|
{** Resets the cursor to the first node.
|
|
|
|
*
|
|
|
|
* Call First before iterating over all nodes. If no nodes are available,
|
|
|
|
* Eof will be set to True.
|
|
|
|
*}
|
|
|
|
procedure First();
|
|
|
|
|
|
|
|
{** Sets the cursor to the next node.
|
|
|
|
*
|
|
|
|
* Call Next while iterating over all nodes. If no more nodes are available,
|
|
|
|
* Eof will be set to True.
|
|
|
|
*}
|
|
|
|
procedure Next();
|
|
|
|
|
|
|
|
{** Returns the current key.
|
|
|
|
*
|
|
|
|
* Note: CurrentKey is only available when the cursor is valid.
|
|
|
|
*}
|
|
|
|
property CurrentKey: Cardinal read GetCurrentKey;
|
|
|
|
|
|
|
|
{** Determines if there are more nodes available.
|
|
|
|
*
|
|
|
|
* Read Eof before accessing CurrentKey to determine if the cursor is
|
|
|
|
* positioned at a valid node.
|
|
|
|
*}
|
|
|
|
property Eof: Boolean read GetEof;
|
|
|
|
end;
|
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
{** Binary Tree with integer data.
|
|
|
|
*
|
|
|
|
* Extends the standard Binary Tree, allowing it to store an Integer value
|
|
|
|
* for each node in the tree.
|
|
|
|
*}
|
|
|
|
TX2IntegerTree = class(TX2BinaryTree)
|
|
|
|
private
|
|
|
|
function GetItem(const AKey: Cardinal): Integer;
|
|
|
|
procedure SetItem(const AKey: Cardinal; const Value: Integer);
|
|
|
|
function GetCurrentValue: Integer;
|
|
|
|
public
|
|
|
|
constructor Create(); override;
|
|
|
|
|
|
|
|
property CurrentValue: Integer read GetCurrentValue;
|
|
|
|
property Items[const AKey: Cardinal]: Integer read GetItem
|
|
|
|
write SetItem; default;
|
|
|
|
end;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
implementation
|
|
|
|
resourcestring
|
|
|
|
RSBTKeyExists = 'The key "%d" already exists in the tree.';
|
|
|
|
RSBTKeyNotFound = 'The key "%d" could not be found in the tree.';
|
|
|
|
RSBTCursorEof = 'Cursor is at Eof.';
|
2005-01-12 15:47:32 +01:00
|
|
|
RSBTNoCursor = 'Cursor not initialized, call First before Next.';
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{===================== TX2BTDefaultCursor
|
|
|
|
Traversal
|
2004-12-28 15:43:27 +01:00
|
|
|
========================================}
|
2005-01-12 15:47:32 +01:00
|
|
|
constructor TX2BTDefaultCursor.Create;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
inherited Create();
|
|
|
|
|
|
|
|
FRoot := ARoot;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TX2BTDefaultCursor.First;
|
|
|
|
begin
|
|
|
|
FNode := FRoot;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TX2BTDefaultCursor.Next;
|
|
|
|
var
|
|
|
|
pChild: PX2BTNode;
|
|
|
|
|
|
|
|
begin
|
|
|
|
if Eof then
|
|
|
|
raise EBTCursorEof.Create(RSBTCursorEof);
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(FNode^.Left) then
|
2004-12-28 15:43:27 +01:00
|
|
|
// Node has a left child
|
2005-01-12 14:06:05 +01:00
|
|
|
FNode := FNode^.Left
|
|
|
|
else if Assigned(FNode^.Right) then
|
2004-12-28 15:43:27 +01:00
|
|
|
// Node has a right child
|
2005-01-12 14:06:05 +01:00
|
|
|
FNode := FNode^.Right
|
2004-12-28 15:43:27 +01:00
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Traverse up the path. If we encounter a left direction, it means we
|
|
|
|
// can attempt to search the right part of that parent node.
|
|
|
|
repeat
|
2005-01-12 14:06:05 +01:00
|
|
|
pChild := FNode;
|
|
|
|
FNode := FNode^.Parent;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(FNode) then
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
if (FNode^.Left = pChild) and Assigned(FNode^.Right) then
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
FNode := FNode^.Right;
|
2004-12-28 15:43:27 +01:00
|
|
|
break;
|
|
|
|
end;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
FNode := nil;
|
|
|
|
break;
|
|
|
|
end;
|
|
|
|
until False;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TX2BTDefaultCursor.GetCurrentNode;
|
|
|
|
begin
|
|
|
|
Result := FNode;
|
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultCursor.GetCurrentKey;
|
|
|
|
begin
|
|
|
|
Result := CurrentNode^.Key;
|
|
|
|
end;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
function TX2BTDefaultCursor.GetEof;
|
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
Result := not Assigned(FNode);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{===================== TX2BTCustomManager
|
2004-12-28 15:43:27 +01:00
|
|
|
Initialization
|
|
|
|
========================================}
|
2005-01-12 15:47:32 +01:00
|
|
|
constructor TX2BTCustomManager.Create;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
destructor TX2BTCustomManager.Destroy;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
Clear();
|
2004-12-28 15:43:27 +01:00
|
|
|
FreeAndNil(FCursor);
|
|
|
|
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTCustomManager.First;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
CursorNeeded();
|
|
|
|
FCursor.First();
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTCustomManager.Next;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
CursorNeeded();
|
|
|
|
FCursor.Next();
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTCustomManager.GetCurrentKey;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
if FCursor.Eof then
|
|
|
|
raise EBTCursorEof.Create(RSBTCursorEof);
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
Result := FCursor.CurrentKey;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTCustomManager.GetEof;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
Result := Assigned(FCursor) and (FCursor.Eof);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
|
|
|
|
{==================== TX2BTDefaultManager
|
|
|
|
Node Management
|
2004-12-28 15:43:27 +01:00
|
|
|
========================================}
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.AllocateNode;
|
2005-01-12 19:56:31 +01:00
|
|
|
var
|
|
|
|
iSize: Cardinal;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
iSize := GetNodeSize() + FDataSize;
|
|
|
|
GetMem(ANode, iSize);
|
|
|
|
FillChar(ANode^, iSize, #0);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.DeallocateNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
FreeMem(ANode, GetNodeSize() + FDataSize);
|
2004-12-28 15:43:27 +01:00
|
|
|
ANode := nil;
|
|
|
|
end;
|
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
function TX2BTDefaultManager.GetNodeSize;
|
|
|
|
begin
|
|
|
|
Result := SizeOf(TX2BTNode);
|
|
|
|
end;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.Clear;
|
2004-12-28 15:43:27 +01:00
|
|
|
var
|
|
|
|
pNode: PX2BTNode;
|
|
|
|
pParent: PX2BTNode;
|
|
|
|
|
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
pNode := FRoot;;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
while Assigned(pNode) do
|
|
|
|
begin
|
|
|
|
if Assigned(pNode^.Left) then
|
|
|
|
// Move down on the left side
|
|
|
|
pNode := pNode^.Left
|
|
|
|
else if Assigned(pNode^.Right) then
|
|
|
|
// Move down on the right side
|
|
|
|
pNode := pNode^.Right
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
// Disconnect node from parent
|
|
|
|
pParent := pNode^.Parent;
|
|
|
|
if Assigned(pParent) then
|
2005-01-12 12:08:24 +01:00
|
|
|
if LeftChild(pNode) then
|
2004-12-28 15:43:27 +01:00
|
|
|
pParent^.Left := nil
|
|
|
|
else
|
|
|
|
pParent^.Right := nil;
|
|
|
|
|
|
|
|
DeallocateNode(pNode);
|
|
|
|
|
|
|
|
// Continue on the parent
|
|
|
|
if Assigned(pParent) then
|
|
|
|
pNode := pParent;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
FLastNode := nil;
|
2005-01-12 14:06:05 +01:00
|
|
|
FRoot := nil;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.FindHighestNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
Result := ANode;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
while Assigned(Result) and Assigned(Result^.Right) do
|
|
|
|
Result := Result^.Right;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.FindLowestNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
Result := ANode;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
while Assigned(Result) and Assigned(Result^.Left) do
|
|
|
|
Result := Result^.Left;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.FindNode;
|
2005-01-12 14:06:05 +01:00
|
|
|
var
|
|
|
|
pNode: PX2BTNode;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
// Quick check; was this node found previously
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(FLastNode) and (FLastNode^.Key = AKey) then
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
Result := FLastNode;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
AParent := nil;
|
|
|
|
FLastNode := nil;
|
2005-01-12 14:06:05 +01:00
|
|
|
Result := nil;
|
|
|
|
pNode := FRoot;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
while Assigned(pNode) do
|
|
|
|
if AKey = pNode^.Key then
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
Result := pNode;
|
|
|
|
break;
|
|
|
|
end else
|
|
|
|
begin
|
|
|
|
AParent := pNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
if AKey < pNode^.Key then
|
|
|
|
pNode := pNode^.Left
|
2004-12-28 15:43:27 +01:00
|
|
|
else
|
2005-01-12 14:06:05 +01:00
|
|
|
pNode := pNode^.Right;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(Result) then
|
2004-12-28 15:43:27 +01:00
|
|
|
FLastNode := Result;
|
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.FindNodeOnly;
|
2004-12-28 15:43:27 +01:00
|
|
|
var
|
2005-01-12 14:06:05 +01:00
|
|
|
pDummy: PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
begin
|
|
|
|
Result := FindNode(AKey, pDummy);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.LeftChild;
|
2005-01-12 12:08:24 +01:00
|
|
|
begin
|
|
|
|
Assert(Assigned(ANode^.Parent), 'Node has no parent!');
|
|
|
|
Result := (ANode^.Parent^.Left = ANode);
|
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.RightChild;
|
2005-01-12 12:08:24 +01:00
|
|
|
begin
|
|
|
|
Result := not LeftChild(ANode);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.SwapNodes;
|
2005-01-12 14:06:05 +01:00
|
|
|
procedure FixLinks(const ANode, AOld: PX2BTNode);
|
|
|
|
begin
|
|
|
|
if Assigned(ANode^.Parent) then
|
|
|
|
if ANode^.Parent^.Left = AOld then
|
|
|
|
ANode^.Parent^.Left := ANode
|
|
|
|
else
|
|
|
|
ANode^.Parent^.Right := ANode;
|
|
|
|
|
|
|
|
if Assigned(ANode^.Left) then
|
|
|
|
ANode^.Left^.Parent := ANode;
|
|
|
|
|
|
|
|
if Assigned(ANode^.Right) then
|
|
|
|
ANode^.Right^.Parent := ANode;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
pBuffer: TX2BTLinks;
|
|
|
|
|
|
|
|
begin
|
|
|
|
pBuffer := ANode1.Links;
|
|
|
|
ANode1.Links := ANode2.Links;
|
|
|
|
ANode2.Links := pBuffer;
|
|
|
|
|
|
|
|
FixLinks(ANode1, ANode2);
|
|
|
|
FixLinks(ANode2, ANode1);
|
|
|
|
|
|
|
|
if FRoot = ANode1 then
|
|
|
|
FRoot := ANode2
|
|
|
|
else if FRoot = ANode2 then
|
|
|
|
FRoot := ANode1;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
function TX2BTDefaultManager.Insert;
|
2004-12-28 15:43:27 +01:00
|
|
|
var
|
2005-01-12 14:06:05 +01:00
|
|
|
pNode: PX2BTNode;
|
|
|
|
pParent: PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
Result := False;
|
|
|
|
pNode := FindNode(AKey, pParent);
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(pNode) then
|
2005-01-12 19:56:31 +01:00
|
|
|
exit;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
Result := True;
|
2005-01-12 14:06:05 +01:00
|
|
|
InvalidateCursor();
|
|
|
|
AllocateNode(pNode);
|
2004-12-28 15:43:27 +01:00
|
|
|
FLastNode := pNode;
|
2005-01-12 14:06:05 +01:00
|
|
|
if not Assigned(FRoot) then
|
|
|
|
FRoot := pNode;
|
|
|
|
|
|
|
|
pNode^.Key := AKey;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
if Assigned(pParent) then
|
2005-01-12 14:06:05 +01:00
|
|
|
begin
|
|
|
|
pNode^.Parent := pParent;
|
|
|
|
|
|
|
|
if AKey < pParent^.Key then
|
|
|
|
pParent^.Left := pNode
|
|
|
|
else
|
|
|
|
pParent^.Right := pNode;
|
|
|
|
end;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
function TX2BTDefaultManager.Delete;
|
2004-12-28 15:43:27 +01:00
|
|
|
var
|
2005-01-12 14:06:05 +01:00
|
|
|
pNode: PX2BTNode;
|
|
|
|
pLowest: PX2BTNode;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
Result := False;
|
|
|
|
pNode := FindNodeOnly(AKey);
|
2005-01-12 14:06:05 +01:00
|
|
|
if not Assigned(pNode) then
|
2005-01-12 19:56:31 +01:00
|
|
|
exit;
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
Result := True;
|
2005-01-12 14:06:05 +01:00
|
|
|
InvalidateCursor();
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
// If the node to be deleted has either one or no branch, it can simply be
|
|
|
|
// taken out of the chain. If it has two branches, find the lowest key on
|
|
|
|
// the right branch and swap it.
|
|
|
|
//
|
|
|
|
// Ex. delete 7 from the tree:
|
|
|
|
//
|
|
|
|
// 8 8
|
|
|
|
// 7 <-+ 4
|
|
|
|
// 2 5 | >>> 2 5
|
|
|
|
// 1 3 4 6 | 1 3 6
|
|
|
|
// +----+
|
2005-01-12 14:06:05 +01:00
|
|
|
if Assigned(pNode^.Left) and Assigned(pNode^.Right) then
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
pLowest := FindLowestNode(pNode^.Right);
|
|
|
|
SwapNodes(pNode, pLowest);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
// At this point, the node is a leaf node or has only one branch
|
2005-01-12 14:06:05 +01:00
|
|
|
DeleteCleanNode(pNode);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.DeleteCleanNode;
|
2005-01-12 12:08:24 +01:00
|
|
|
var
|
|
|
|
pParent: PX2BTNode;
|
|
|
|
pChild: PX2BTNode;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 12:08:24 +01:00
|
|
|
pParent := ANode^.Parent;
|
|
|
|
|
|
|
|
// A 'clean' node is defined as a node with 0 or 1 child, which is easy
|
|
|
|
// to remove from the chain.
|
|
|
|
Assert(not (Assigned(ANode^.Left) and
|
|
|
|
Assigned(ANode^.Right)), 'Node is not a clean node!');
|
|
|
|
|
|
|
|
if Assigned(ANode^.Left) then
|
|
|
|
pChild := ANode^.Left
|
|
|
|
else
|
|
|
|
pChild := ANode^.Right;
|
|
|
|
|
|
|
|
// Link the parent to the new child
|
|
|
|
if Assigned(pParent) then
|
|
|
|
if LeftChild(ANode) then
|
|
|
|
pParent^.Left := pChild
|
|
|
|
else
|
|
|
|
pParent^.Right := pChild;
|
|
|
|
|
|
|
|
// Link the child to the new parent
|
|
|
|
if Assigned(pChild) then
|
|
|
|
pChild^.Parent := pParent;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
if ANode = FRoot then
|
|
|
|
FRoot := pChild;
|
|
|
|
|
|
|
|
DeallocateNode(ANode);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BTDefaultManager.Exists;
|
|
|
|
begin
|
|
|
|
Result := Assigned(FindNodeOnly(AKey));
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TX2BTDefaultManager.GetData;
|
2005-01-12 19:56:31 +01:00
|
|
|
var
|
|
|
|
pNode: PX2BTNode;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
pNode := FindNodeOnly(AKey);
|
|
|
|
if not Assigned(pNode) then
|
|
|
|
raise EBTKeyNotFound.CreateFmt(RSBTKeyNotFound, [AKey]);
|
|
|
|
|
|
|
|
Result := Pointer(Cardinal(pNode) + GetNodeSize());
|
2005-01-12 15:47:32 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
procedure TX2BTDefaultManager.CursorNeeded;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
|
|
|
if not Assigned(FCursor) then
|
2005-01-12 15:47:32 +01:00
|
|
|
FCursor := TX2BTDefaultCursor.Create(FRoot);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
procedure TX2BTDefaultManager.InvalidateCursor;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 14:06:05 +01:00
|
|
|
FreeAndNil(FCursor);
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{========================== TX2BinaryTree
|
|
|
|
Initialization
|
|
|
|
========================================}
|
|
|
|
constructor TX2BinaryTree.Create;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
FManager := GetManagerClass().Create();
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TX2BinaryTree.Destroy;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
FreeAndNil(FManager);
|
|
|
|
|
|
|
|
inherited;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 14:06:05 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
{========================== TX2BinaryTree
|
|
|
|
Interface
|
|
|
|
========================================}
|
|
|
|
procedure TX2BinaryTree.Clear;
|
2004-12-28 15:43:27 +01:00
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
FManager.Clear();
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TX2BinaryTree.Exists;
|
|
|
|
begin
|
|
|
|
Result := FManager.Exists(AKey);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TX2BinaryTree.Insert;
|
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
if not FManager.Insert(AKey) then
|
|
|
|
raise EBTKeyExists.CreateFmt(RSBTKeyExists, [AKey]);
|
2005-01-12 15:47:32 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TX2BinaryTree.Delete;
|
|
|
|
begin
|
2005-01-12 19:56:31 +01:00
|
|
|
if not FManager.Delete(AKey) then
|
|
|
|
raise EBTKeyNotFound.CreateFmt(RSBTKeyNotFound, [AKey]);
|
2005-01-12 15:47:32 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TX2BinaryTree.First;
|
|
|
|
begin
|
|
|
|
FManager.First();
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TX2BinaryTree.Next;
|
|
|
|
begin
|
|
|
|
FManager.Next();
|
|
|
|
end;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
|
2005-01-12 15:47:32 +01:00
|
|
|
function TX2BinaryTree.GetManagerClass;
|
|
|
|
begin
|
|
|
|
Result := TX2BTDefaultManager;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TX2BinaryTree.GetCurrentKey;
|
|
|
|
begin
|
|
|
|
Result := FManager.CurrentKey;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TX2BinaryTree.GetEof;
|
|
|
|
begin
|
2005-01-12 15:47:32 +01:00
|
|
|
Result := FManager.Eof;
|
2004-12-28 15:43:27 +01:00
|
|
|
end;
|
|
|
|
|
2005-01-12 19:56:31 +01:00
|
|
|
|
|
|
|
{========================= TX2IntegerTree
|
|
|
|
Initialization
|
|
|
|
========================================}
|
|
|
|
constructor TX2IntegerTree.Create;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
FManager.DataSize := SizeOf(Integer);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TX2IntegerTree.GetCurrentValue;
|
|
|
|
begin
|
|
|
|
Result := GetItem(FManager.CurrentKey);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TX2IntegerTree.GetItem;
|
|
|
|
begin
|
|
|
|
Result := PInteger(FManager.GetData(AKey))^;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TX2IntegerTree.SetItem;
|
|
|
|
begin
|
|
|
|
FManager.Insert(AKey);
|
|
|
|
PInteger(FManager.GetData(AKey))^ := Value;
|
|
|
|
end;
|
|
|
|
|
2004-12-28 15:43:27 +01:00
|
|
|
end.
|