mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 17:52:36 +02:00
967 lines
25 KiB
ObjectPascal
967 lines
25 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TAVLTree is an Average Level binary Tree. This binary tree is always
|
|
balanced, so that inserting, deleting and finding a node is performed in
|
|
O(log(#Nodes)).
|
|
}
|
|
unit AVL_Tree;
|
|
|
|
{$ifdef FPC}{$mode objfpc}{$endif}{$H+}
|
|
|
|
interface
|
|
|
|
uses Classes, SysUtils;
|
|
|
|
type
|
|
TAVLTreeNode = class
|
|
public
|
|
Parent, Left, Right: TAVLTreeNode;
|
|
Balance: integer;
|
|
Data: Pointer;
|
|
procedure Clear;
|
|
function TreeDepth: integer; // longest WAY down. e.g. only one node => 0 !
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TAVLTree = class
|
|
private
|
|
FOnCompare: TListSortCompare;
|
|
FCount: integer;
|
|
procedure BalanceAfterInsert(ANode: TAVLTreeNode);
|
|
procedure BalanceAfterDelete(ANode: TAVLTreeNode);
|
|
function FindInsertPos(Data: Pointer): TAVLTreeNode;
|
|
public
|
|
Root: TAVLTreeNode;
|
|
function Find(Data: Pointer): TAVLTreeNode;
|
|
function FindSuccessor(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
function FindPrecessor(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
function FindLowest: TAVLTreeNode;
|
|
function FindHighest: TAVLTreeNode;
|
|
procedure Add(ANode: TAVLTreeNode);
|
|
function Add(Data: Pointer): TAVLTreeNode;
|
|
procedure Delete(ANode: TAVLTreeNode);
|
|
procedure MoveDataLeftMost(var ANode: TAVLTreeNode);
|
|
procedure MoveDataRightMost(var ANode: TAVLTreeNode);
|
|
property OnCompare: TListSortCompare read FOnCompare write FOnCompare;
|
|
procedure Clear;
|
|
procedure FreeAndClear;
|
|
procedure FreeAndDelete(ANode: TAVLTreeNode);
|
|
property Count: integer read FCount;
|
|
function ConsistencyCheck: integer;
|
|
procedure WriteReportToStream(s: TStream; var StreamSize: integer);
|
|
function ReportAsString: string;
|
|
constructor Create(OnCompareMethod: TListSortCompare);
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TAVLTreeNodeMemManager = class
|
|
private
|
|
FFirstFree: TAVLTreeNode;
|
|
FFreeCount: integer;
|
|
FCount: integer;
|
|
FMinFree: integer;
|
|
FMaxFreeRatio: integer;
|
|
procedure SetMaxFreeRatio(NewValue: integer);
|
|
procedure SetMinFree(NewValue: integer);
|
|
public
|
|
procedure DisposeNode(ANode: TAVLTreeNode);
|
|
function NewNode: TAVLTreeNode;
|
|
property MinimumFreeNode: integer read FMinFree write SetMinFree;
|
|
property MaximumFreeNodeRatio: integer
|
|
read FMaxFreeRatio write SetMaxFreeRatio; // in one eighth steps
|
|
property Count: integer read FCount;
|
|
procedure Clear;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
|
|
var NodeMemManager: TAVLTreeNodeMemManager;
|
|
|
|
{ TAVLTree }
|
|
|
|
function TAVLTree.Add(Data: Pointer): TAVLTreeNode;
|
|
begin
|
|
Result:=NodeMemManager.NewNode;
|
|
Result.Data:=Data;
|
|
Add(Result);
|
|
end;
|
|
|
|
procedure TAVLTree.Add(ANode: TAVLTreeNode);
|
|
// add a node. If there are already nodes with the same value it will be
|
|
// inserted rightmost
|
|
var InsertPos: TAVLTreeNode;
|
|
InsertComp: integer;
|
|
begin
|
|
ANode.Left:=nil;
|
|
ANode.Right:=nil;
|
|
inc(FCount);
|
|
if Root<>nil then begin
|
|
InsertPos:=FindInsertPos(ANode.Data);
|
|
InsertComp:=OnCompare(ANode.Data,InsertPos.Data);
|
|
ANode.Parent:=InsertPos;
|
|
if InsertComp<0 then begin
|
|
// insert to the left
|
|
InsertPos.Left:=ANode;
|
|
end else begin
|
|
// insert to the right
|
|
InsertPos.Right:=ANode;
|
|
end;
|
|
BalanceAfterInsert(ANode);
|
|
end else begin
|
|
Root:=ANode;
|
|
ANode.Parent:=nil;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTree.FindLowest: TAVLTreeNode;
|
|
begin
|
|
Result:=Root;
|
|
if Result<>nil then
|
|
while Result.Left<>nil do Result:=Result.Left;
|
|
end;
|
|
|
|
function TAVLTree.FindHighest: TAVLTreeNode;
|
|
begin
|
|
Result:=Root;
|
|
if Result<>nil then
|
|
while Result.Right<>nil do Result:=Result.Right;
|
|
end;
|
|
|
|
procedure TAVLTree.BalanceAfterDelete(ANode: TAVLTreeNode);
|
|
var OldParent, OldRight, OldRightLeft, OldLeft, OldLeftRight,
|
|
OldRightLeftLeft, OldRightLeftRight, OldLeftRightLeft, OldLeftRightRight
|
|
: TAVLTreeNode;
|
|
begin
|
|
if (ANode=nil) then exit;
|
|
if ((ANode.Balance=+1) or (ANode.Balance=-1)) then exit;
|
|
OldParent:=ANode.Parent;
|
|
if (ANode.Balance=0) then begin
|
|
// Treeheight has decreased by one
|
|
if (OldParent<>nil) then begin
|
|
if(OldParent.Left=ANode) then
|
|
Inc(OldParent.Balance)
|
|
else
|
|
Dec(OldParent.Balance);
|
|
BalanceAfterDelete(OldParent);
|
|
end;
|
|
exit;
|
|
end;
|
|
if (ANode.Balance=+2) then begin
|
|
// Node is overweighted to the right
|
|
OldRight:=ANode.Right;
|
|
if (OldRight.Balance>=0) then begin
|
|
// OldRight.Balance=={0 or -1}
|
|
// rotate left
|
|
OldRightLeft:=OldRight.Left;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then
|
|
OldParent.Left:=OldRight
|
|
else
|
|
OldParent.Right:=OldRight;
|
|
end else
|
|
Root:=OldRight;
|
|
ANode.Parent:=OldRight;
|
|
ANode.Right:=OldRightLeft;
|
|
OldRight.Parent:=OldParent;
|
|
OldRight.Left:=ANode;
|
|
if (OldRightLeft<>nil) then
|
|
OldRightLeft.Parent:=ANode;
|
|
ANode.Balance:=(1-OldRight.Balance);
|
|
Dec(OldRight.Balance);
|
|
BalanceAfterDelete(OldRight);
|
|
end else begin
|
|
// OldRight.Balance=-1
|
|
// double rotate right left
|
|
OldRightLeft:=OldRight.Left;
|
|
OldRightLeftLeft:=OldRightLeft.Left;
|
|
OldRightLeftRight:=OldRightLeft.Right;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then
|
|
OldParent.Left:=OldRightLeft
|
|
else
|
|
OldParent.Right:=OldRightLeft;
|
|
end else
|
|
Root:=OldRightLeft;
|
|
ANode.Parent:=OldRightLeft;
|
|
ANode.Right:=OldRightLeftLeft;
|
|
OldRight.Parent:=OldRightLeft;
|
|
OldRight.Left:=OldRightLeftRight;
|
|
OldRightLeft.Parent:=OldParent;
|
|
OldRightLeft.Left:=ANode;
|
|
OldRightLeft.Right:=OldRight;
|
|
if (OldRightLeftLeft<>nil) then
|
|
OldRightLeftLeft.Parent:=ANode;
|
|
if (OldRightLeftRight<>nil) then
|
|
OldRightLeftRight.Parent:=OldRight;
|
|
if (OldRightLeft.Balance<=0) then
|
|
ANode.Balance:=0
|
|
else
|
|
ANode.Balance:=-1;
|
|
if (OldRightLeft.Balance>=0) then
|
|
OldRight.Balance:=0
|
|
else
|
|
OldRight.Balance:=+1;
|
|
OldRightLeft.Balance:=0;
|
|
BalanceAfterDelete(OldRightLeft);
|
|
end;
|
|
end else begin
|
|
// Node.Balance=-2
|
|
// Node is overweighted to the left
|
|
OldLeft:=ANode.Left;
|
|
if (OldLeft.Balance<=0) then begin
|
|
// rotate right
|
|
OldLeftRight:=OldLeft.Right;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then
|
|
OldParent.Left:=OldLeft
|
|
else
|
|
OldParent.Right:=OldLeft;
|
|
end else
|
|
Root:=OldLeft;
|
|
ANode.Parent:=OldLeft;
|
|
ANode.Left:=OldLeftRight;
|
|
OldLeft.Parent:=OldParent;
|
|
OldLeft.Right:=ANode;
|
|
if (OldLeftRight<>nil) then
|
|
OldLeftRight.Parent:=ANode;
|
|
ANode.Balance:=(-1-OldLeft.Balance);
|
|
Inc(OldLeft.Balance);
|
|
BalanceAfterDelete(OldLeft);
|
|
end else begin
|
|
// OldLeft.Balance = 1
|
|
// double rotate left right
|
|
OldLeftRight:=OldLeft.Right;
|
|
OldLeftRightLeft:=OldLeftRight.Left;
|
|
OldLeftRightRight:=OldLeftRight.Right;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then
|
|
OldParent.Left:=OldLeftRight
|
|
else
|
|
OldParent.Right:=OldLeftRight;
|
|
end else
|
|
Root:=OldLeftRight;
|
|
ANode.Parent:=OldLeftRight;
|
|
ANode.Left:=OldLeftRightRight;
|
|
OldLeft.Parent:=OldLeftRight;
|
|
OldLeft.Right:=OldLeftRightLeft;
|
|
OldLeftRight.Parent:=OldParent;
|
|
OldLeftRight.Left:=OldLeft;
|
|
OldLeftRight.Right:=ANode;
|
|
if (OldLeftRightLeft<>nil) then
|
|
OldLeftRightLeft.Parent:=OldLeft;
|
|
if (OldLeftRightRight<>nil) then
|
|
OldLeftRightRight.Parent:=ANode;
|
|
if (OldLeftRight.Balance>=0) then
|
|
ANode.Balance:=0
|
|
else
|
|
ANode.Balance:=+1;
|
|
if (OldLeftRight.Balance<=0) then
|
|
OldLeft.Balance:=0
|
|
else
|
|
OldLeft.Balance:=-1;
|
|
OldLeftRight.Balance:=0;
|
|
BalanceAfterDelete(OldLeftRight);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TAVLTree.BalanceAfterInsert(ANode: TAVLTreeNode);
|
|
var OldParent, OldParentParent, OldRight, OldRightLeft, OldRightRight, OldLeft,
|
|
OldLeftLeft, OldLeftRight: TAVLTreeNode;
|
|
begin
|
|
OldParent:=ANode.Parent;
|
|
if (OldParent=nil) then exit;
|
|
if (OldParent.Left=ANode) then begin
|
|
// Node is left son
|
|
dec(OldParent.Balance);
|
|
if (OldParent.Balance=0) then exit;
|
|
if (OldParent.Balance=-1) then begin
|
|
BalanceAfterInsert(OldParent);
|
|
exit;
|
|
end;
|
|
// OldParent.Balance=-2
|
|
if (ANode.Balance=-1) then begin
|
|
// rotate
|
|
OldRight:=ANode.Right;
|
|
OldParentParent:=OldParent.Parent;
|
|
if (OldParentParent<>nil) then begin
|
|
// OldParent has GrandParent. GrandParent gets new child
|
|
if (OldParentParent.Left=OldParent) then
|
|
OldParentParent.Left:=ANode
|
|
else
|
|
OldParentParent.Right:=ANode;
|
|
end else begin
|
|
// OldParent was root node. New root node
|
|
Root:=ANode;
|
|
end;
|
|
ANode.Parent:=OldParentParent;
|
|
ANode.Right:=OldParent;
|
|
OldParent.Parent:=ANode;
|
|
OldParent.Left:=OldRight;
|
|
if (OldRight<>nil) then
|
|
OldRight.Parent:=OldParent;
|
|
ANode.Balance:=0;
|
|
OldParent.Balance:=0;
|
|
end else begin
|
|
// Node.Balance = +1
|
|
// double rotate
|
|
OldParentParent:=OldParent.Parent;
|
|
OldRight:=ANode.Right;
|
|
OldRightLeft:=OldRight.Left;
|
|
OldRightRight:=OldRight.Right;
|
|
if (OldParentParent<>nil) then begin
|
|
// OldParent has GrandParent. GrandParent gets new child
|
|
if (OldParentParent.Left=OldParent) then
|
|
OldParentParent.Left:=OldRight
|
|
else
|
|
OldParentParent.Right:=OldRight;
|
|
end else begin
|
|
// OldParent was root node. new root node
|
|
Root:=OldRight;
|
|
end;
|
|
OldRight.Parent:=OldParentParent;
|
|
OldRight.Left:=ANode;
|
|
OldRight.Right:=OldParent;
|
|
ANode.Parent:=OldRight;
|
|
ANode.Right:=OldRightLeft;
|
|
OldParent.Parent:=OldRight;
|
|
OldParent.Left:=OldRightRight;
|
|
if (OldRightLeft<>nil) then
|
|
OldRightLeft.Parent:=ANode;
|
|
if (OldRightRight<>nil) then
|
|
OldRightRight.Parent:=OldParent;
|
|
if (OldRight.Balance<=0) then
|
|
ANode.Balance:=0
|
|
else
|
|
ANode.Balance:=-1;
|
|
if (OldRight.Balance=-1) then
|
|
OldParent.Balance:=1
|
|
else
|
|
OldParent.Balance:=0;
|
|
OldRight.Balance:=0;
|
|
end;
|
|
end else begin
|
|
// Node is right son
|
|
Inc(OldParent.Balance);
|
|
if (OldParent.Balance=0) then exit;
|
|
if (OldParent.Balance=+1) then begin
|
|
BalanceAfterInsert(OldParent);
|
|
exit;
|
|
end;
|
|
// OldParent.Balance = +2
|
|
if(ANode.Balance=+1) then begin
|
|
// rotate
|
|
OldLeft:=ANode.Left;
|
|
OldParentParent:=OldParent.Parent;
|
|
if (OldParentParent<>nil) then begin
|
|
// Parent has GrandParent . GrandParent gets new child
|
|
if(OldParentParent.Left=OldParent) then
|
|
OldParentParent.Left:=ANode
|
|
else
|
|
OldParentParent.Right:=ANode;
|
|
end else begin
|
|
// OldParent was root node . new root node
|
|
Root:=ANode;
|
|
end;
|
|
ANode.Parent:=OldParentParent;
|
|
ANode.Left:=OldParent;
|
|
OldParent.Parent:=ANode;
|
|
OldParent.Right:=OldLeft;
|
|
if (OldLeft<>nil) then
|
|
OldLeft.Parent:=OldParent;
|
|
ANode.Balance:=0;
|
|
OldParent.Balance:=0;
|
|
end else begin
|
|
// Node.Balance = -1
|
|
// double rotate
|
|
OldLeft:=ANode.Left;
|
|
OldParentParent:=OldParent.Parent;
|
|
OldLeftLeft:=OldLeft.Left;
|
|
OldLeftRight:=OldLeft.Right;
|
|
if (OldParentParent<>nil) then begin
|
|
// OldParent has GrandParent . GrandParent gets new child
|
|
if (OldParentParent.Left=OldParent) then
|
|
OldParentParent.Left:=OldLeft
|
|
else
|
|
OldParentParent.Right:=OldLeft;
|
|
end else begin
|
|
// OldParent was root node . new root node
|
|
Root:=OldLeft;
|
|
end;
|
|
OldLeft.Parent:=OldParentParent;
|
|
OldLeft.Left:=OldParent;
|
|
OldLeft.Right:=ANode;
|
|
ANode.Parent:=OldLeft;
|
|
ANode.Left:=OldLeftRight;
|
|
OldParent.Parent:=OldLeft;
|
|
OldParent.Right:=OldLeftLeft;
|
|
if (OldLeftLeft<>nil) then
|
|
OldLeftLeft.Parent:=OldParent;
|
|
if (OldLeftRight<>nil) then
|
|
OldLeftRight.Parent:=ANode;
|
|
if (OldLeft.Balance>=0) then
|
|
ANode.Balance:=0
|
|
else
|
|
ANode.Balance:=+1;
|
|
if (OldLeft.Balance=+1) then
|
|
OldParent.Balance:=-1
|
|
else
|
|
OldParent.Balance:=0;
|
|
OldLeft.Balance:=0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TAVLTree.Clear;
|
|
|
|
procedure DeleteNode(ANode: TAVLTreeNode);
|
|
begin
|
|
if ANode<>nil then begin
|
|
if ANode.Left<>nil then DeleteNode(ANode.Left);
|
|
if ANode.Right<>nil then DeleteNode(ANode.Right);
|
|
end;
|
|
NodeMemManager.DisposeNode(ANode);
|
|
end;
|
|
|
|
// Clear
|
|
begin
|
|
DeleteNode(Root);
|
|
Root:=nil;
|
|
FCount:=0;
|
|
end;
|
|
|
|
constructor TAVLTree.Create(OnCompareMethod: TListSortCompare);
|
|
begin
|
|
inherited Create;
|
|
FOnCompare:=OnCompareMethod;
|
|
FCount:=0;
|
|
end;
|
|
|
|
procedure TAVLTree.Delete(ANode: TAVLTreeNode);
|
|
var OldParent, OldLeft, OldRight, Successor, OldSuccParent, OldSuccLeft,
|
|
OldSuccRight: TAVLTreeNode;
|
|
OldBalance: integer;
|
|
begin
|
|
OldParent:=ANode.Parent;
|
|
OldBalance:=ANode.Balance;
|
|
ANode.Parent:=nil;
|
|
ANode.Balance:=0;
|
|
if ((ANode.Left=nil) and (ANode.Right=nil)) then begin
|
|
// Node is Leaf (no children)
|
|
if (OldParent<>nil) then begin
|
|
// Node has parent
|
|
if (OldParent.Left=ANode) then begin
|
|
// Node is left Son of OldParent
|
|
OldParent.Left:=nil;
|
|
Inc(OldParent.Balance);
|
|
end else begin
|
|
// Node is right Son of OldParent
|
|
OldParent.Right:=nil;
|
|
Dec(OldParent.Balance);
|
|
end;
|
|
BalanceAfterDelete(OldParent);
|
|
end else begin
|
|
// Node is the only node of tree
|
|
Root:=nil;
|
|
end;
|
|
dec(FCount);
|
|
NodeMemManager.DisposeNode(ANode);
|
|
exit;
|
|
end;
|
|
if (ANode.Right=nil) then begin
|
|
// Left is only son
|
|
// and because DelNode is AVL, Right has no childrens
|
|
// replace DelNode with Left
|
|
OldLeft:=ANode.Left;
|
|
ANode.Left:=nil;
|
|
OldLeft.Parent:=OldParent;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then begin
|
|
OldParent.Left:=OldLeft;
|
|
Inc(OldParent.Balance);
|
|
end else begin
|
|
OldParent.Right:=OldLeft;
|
|
Dec(OldParent.Balance);
|
|
end;
|
|
BalanceAfterDelete(OldParent);
|
|
end else begin
|
|
Root:=OldLeft;
|
|
end;
|
|
dec(FCount);
|
|
NodeMemManager.DisposeNode(ANode);
|
|
exit;
|
|
end;
|
|
if (ANode.Left=nil) then begin
|
|
// Right is only son
|
|
// and because DelNode is AVL, Left has no childrens
|
|
// replace DelNode with Right
|
|
OldRight:=ANode.Right;
|
|
ANode.Right:=nil;
|
|
OldRight.Parent:=OldParent;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then begin
|
|
OldParent.Left:=OldRight;
|
|
Inc(OldParent.Balance);
|
|
end else begin
|
|
OldParent.Right:=OldRight;
|
|
Dec(OldParent.Balance);
|
|
end;
|
|
BalanceAfterDelete(OldParent);
|
|
end else begin
|
|
Root:=OldRight;
|
|
end;
|
|
dec(FCount);
|
|
NodeMemManager.DisposeNode(ANode);
|
|
exit;
|
|
end;
|
|
// DelNode has both: Left and Right
|
|
// Replace ANode with symmetric Successor
|
|
Successor:=FindSuccessor(ANode);
|
|
OldLeft:=ANode.Left;
|
|
OldRight:=ANode.Right;
|
|
OldSuccParent:=Successor.Parent;
|
|
OldSuccLeft:=Successor.Left;
|
|
OldSuccRight:=Successor.Right;
|
|
ANode.Balance:=Successor.Balance;
|
|
Successor.Balance:=OldBalance;
|
|
if (OldSuccParent<>ANode) then begin
|
|
// at least one node between ANode and Successor
|
|
ANode.Parent:=Successor.Parent;
|
|
if (OldSuccParent.Left=Successor) then
|
|
OldSuccParent.Left:=ANode
|
|
else
|
|
OldSuccParent.Right:=ANode;
|
|
Successor.Right:=OldRight;
|
|
OldRight.Parent:=Successor;
|
|
end else begin
|
|
// Successor is right son of ANode
|
|
ANode.Parent:=Successor;
|
|
Successor.Right:=ANode;
|
|
end;
|
|
Successor.Left:=OldLeft;
|
|
if OldLeft<>nil then
|
|
OldLeft.Parent:=Successor;
|
|
Successor.Parent:=OldParent;
|
|
ANode.Left:=OldSuccLeft;
|
|
if ANode.Left<>nil then
|
|
ANode.Left.Parent:=ANode;
|
|
ANode.Right:=OldSuccRight;
|
|
if ANode.Right<>nil then
|
|
ANode.Right.Parent:=ANode;
|
|
if (OldParent<>nil) then begin
|
|
if (OldParent.Left=ANode) then
|
|
OldParent.Left:=Successor
|
|
else
|
|
OldParent.Right:=Successor;
|
|
end else
|
|
Root:=Successor;
|
|
// delete Node as usual
|
|
Delete(ANode);
|
|
end;
|
|
|
|
destructor TAVLTree.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TAVLTree.Find(Data: Pointer): TAVLTreeNode;
|
|
var Comp: integer;
|
|
begin
|
|
Result:=Root;
|
|
while (Result<>nil) do begin
|
|
Comp:=OnCompare(Data,Result.Data);
|
|
if Comp=0 then exit;
|
|
if Comp<0 then begin
|
|
if Result.Left<>nil then
|
|
Result:=Result.Left
|
|
else
|
|
exit;
|
|
end else begin
|
|
if Result.Right<>nil then
|
|
Result:=Result.Right
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTree.FindInsertPos(Data: Pointer): TAVLTreeNode;
|
|
var Comp: integer;
|
|
begin
|
|
Result:=Root;
|
|
while (Result<>nil) do begin
|
|
Comp:=OnCompare(Data,Result.Data);
|
|
if Comp<0 then begin
|
|
if Result.Left<>nil then
|
|
Result:=Result.Left
|
|
else
|
|
exit;
|
|
end else begin
|
|
if Result.Right<>nil then
|
|
Result:=Result.Right
|
|
else
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTree.FindSuccessor(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
begin
|
|
Result:=ANode.Right;
|
|
if Result<>nil then begin
|
|
while (Result.Left<>nil) do Result:=Result.Left;
|
|
end else begin
|
|
Result:=ANode;
|
|
while (Result.Parent<>nil) and (Result.Parent.Right=Result) do
|
|
Result:=Result.Parent;
|
|
Result:=Result.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTree.FindPrecessor(ANode: TAVLTreeNode): TAVLTreeNode;
|
|
begin
|
|
Result:=ANode.Left;
|
|
if Result<>nil then begin
|
|
while (Result.Right<>nil) do Result:=Result.Right;
|
|
end else begin
|
|
Result:=ANode;
|
|
while (Result.Parent<>nil) and (Result.Parent.Left=Result) do
|
|
Result:=Result.Parent;
|
|
Result:=Result.Parent;
|
|
end;
|
|
end;
|
|
|
|
procedure TAVLTree.MoveDataLeftMost(var ANode: TAVLTreeNode);
|
|
var LeftMost, PreNode: TAVLTreeNode;
|
|
Data: Pointer;
|
|
begin
|
|
if ANode=nil then exit;
|
|
LeftMost:=ANode;
|
|
repeat
|
|
PreNode:=FindPrecessor(LeftMost);
|
|
if (PreNode=nil) or (FOnCompare(ANode,PreNode)<>0) then break;
|
|
LeftMost:=PreNode;
|
|
until false;
|
|
if LeftMost=ANode then exit;
|
|
Data:=LeftMost.Data;
|
|
LeftMost.Data:=ANode.Data;
|
|
ANode.Data:=Data;
|
|
ANode:=LeftMost;
|
|
end;
|
|
|
|
procedure TAVLTree.MoveDataRightMost(var ANode: TAVLTreeNode);
|
|
var RightMost, PostNode: TAVLTreeNode;
|
|
Data: Pointer;
|
|
begin
|
|
if ANode=nil then exit;
|
|
RightMost:=ANode;
|
|
repeat
|
|
PostNode:=FindSuccessor(RightMost);
|
|
if (PostNode=nil) or (FOnCompare(ANode,PostNode)<>0) then break;
|
|
RightMost:=PostNode;
|
|
until false;
|
|
if RightMost=ANode then exit;
|
|
Data:=RightMost.Data;
|
|
RightMost.Data:=ANode.Data;
|
|
ANode.Data:=Data;
|
|
ANode:=RightMost;
|
|
end;
|
|
|
|
function TAVLTree.ConsistencyCheck: integer;
|
|
var RealCount: integer;
|
|
|
|
function CheckNode(ANode: TAVLTreeNode): integer;
|
|
var LeftDepth, RightDepth: integer;
|
|
begin
|
|
if ANode=nil then begin
|
|
Result:=0;
|
|
exit;
|
|
end;
|
|
inc(RealCount);
|
|
// test left son
|
|
if ANode.Left<>nil then begin
|
|
if ANode.Left.Parent<>ANode then begin
|
|
Result:=-2; exit;
|
|
end;
|
|
if OnCompare(ANode.Left.Data,ANode.Data)>0 then begin
|
|
Result:=-3; exit;
|
|
end;
|
|
Result:=CheckNode(ANode.Left);
|
|
if Result<>0 then exit;
|
|
end;
|
|
// test right son
|
|
if ANode.Right<>nil then begin
|
|
if ANode.Right.Parent<>ANode then begin
|
|
Result:=-4; exit;
|
|
end;
|
|
if OnCompare(ANode.Data,ANode.Right.Data)>0 then begin
|
|
Result:=-5; exit;
|
|
end;
|
|
Result:=CheckNode(ANode.Right);
|
|
if Result<>0 then exit;
|
|
end;
|
|
// test balance
|
|
if ANode.Left<>nil then
|
|
LeftDepth:=ANode.Left.TreeDepth+1
|
|
else
|
|
LeftDepth:=0;
|
|
if ANode.Right<>nil then
|
|
RightDepth:=ANode.Right.TreeDepth+1
|
|
else
|
|
RightDepth:=0;
|
|
if ANode.Balance<>(RightDepth-LeftDepth) then begin
|
|
Result:=-6; exit;
|
|
end;
|
|
// ok
|
|
Result:=0;
|
|
end;
|
|
|
|
// TAVLTree.ConsistencyCheck
|
|
begin
|
|
RealCount:=0;
|
|
Result:=CheckNode(Root);
|
|
if Result<>0 then exit;
|
|
if FCount<>RealCount then begin
|
|
Result:=-1;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TAVLTree.FreeAndClear;
|
|
|
|
procedure FreeNode(ANode: TAVLTreeNode);
|
|
begin
|
|
if ANode=nil then exit;
|
|
FreeNode(ANode.Left);
|
|
FreeNode(ANode.Right);
|
|
if ANode.Data<>nil then TObject(ANode.Data).Free;
|
|
ANode.Data:=nil;
|
|
end;
|
|
|
|
// TAVLTree.FreeAndClear
|
|
begin
|
|
// free all data
|
|
FreeNode(Root);
|
|
// free all nodes
|
|
Clear;
|
|
end;
|
|
|
|
procedure TAVLTree.FreeAndDelete(ANode: TAVLTreeNode);
|
|
var OldData: TObject;
|
|
begin
|
|
OldData:=TObject(ANode.Data);
|
|
Delete(ANode);
|
|
OldData.Free;
|
|
end;
|
|
|
|
procedure TAVLTree.WriteReportToStream(s: TStream; var StreamSize: integer);
|
|
var h: string;
|
|
|
|
procedure WriteStr(const Txt: string);
|
|
begin
|
|
if s<>nil then
|
|
s.Write(Txt[1],length(Txt));
|
|
inc(StreamSize,length(Txt));
|
|
end;
|
|
|
|
procedure WriteTreeNode(ANode: TAVLTreeNode; const Prefix: string);
|
|
var b: string;
|
|
begin
|
|
if ANode=nil then exit;
|
|
WriteTreeNode(ANode.Right,Prefix+' ');
|
|
b:=Prefix+HexStr(Cardinal(ANode.Data),8)+' '
|
|
+' Self='+HexStr(Cardinal(ANode),8)
|
|
+' Parent='+HexStr(Cardinal(ANode.Parent),8)
|
|
+' Balance='+IntToStr(ANode.Balance)
|
|
+#13#10;
|
|
WriteStr(b);
|
|
WriteTreeNode(ANode.Left,Prefix+' ');
|
|
end;
|
|
|
|
// TAVLTree.WriteReportToStream
|
|
begin
|
|
h:='Consistency: '+IntToStr(ConsistencyCheck)+' ---------------------'+#13#10;
|
|
WriteStr(h);
|
|
WriteTreeNode(Root,' ');
|
|
h:='-End-Of-AVL-Tree---------------------'+#13#10;
|
|
WriteStr(h);
|
|
end;
|
|
|
|
function TAVLTree.ReportAsString: string;
|
|
var ms: TMemoryStream;
|
|
StreamSize: integer;
|
|
begin
|
|
Result:='';
|
|
ms:=TMemoryStream.Create;
|
|
try
|
|
StreamSize:=0;
|
|
WriteReportToStream(nil,StreamSize);
|
|
ms.Size:=StreamSize;
|
|
StreamSize:=0;
|
|
WriteReportToStream(ms,StreamSize);
|
|
if ms.Size>0 then begin
|
|
ms.Position:=0;
|
|
SetLength(Result,ms.Size);
|
|
ms.Read(Result[1],ms.Size);
|
|
end;
|
|
finally
|
|
ms.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TAVLTreeNode }
|
|
|
|
constructor TAVLTreeNode.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
end;
|
|
|
|
destructor TAVLTreeNode.Destroy;
|
|
begin
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TAVLTreeNode.TreeDepth: integer;
|
|
// longest WAY down. e.g. only one node => 0 !
|
|
var LeftDepth, RightDepth: integer;
|
|
begin
|
|
if Left<>nil then
|
|
LeftDepth:=Left.TreeDepth+1
|
|
else
|
|
LeftDepth:=0;
|
|
if Right<>nil then
|
|
RightDepth:=Right.TreeDepth+1
|
|
else
|
|
RightDepth:=0;
|
|
if LeftDepth>RightDepth then
|
|
Result:=LeftDepth
|
|
else
|
|
Result:=RightDepth;
|
|
end;
|
|
|
|
procedure TAVLTreeNode.Clear;
|
|
begin
|
|
Parent:=nil;
|
|
Left:=nil;
|
|
Right:=nil;
|
|
Balance:=0;
|
|
Data:=nil;
|
|
end;
|
|
|
|
{ TAVLTreeNodeMemManager }
|
|
|
|
constructor TAVLTreeNodeMemManager.Create;
|
|
begin
|
|
inherited Create;
|
|
FFirstFree:=nil;
|
|
FFreeCount:=0;
|
|
FCount:=0;
|
|
FMinFree:=100;
|
|
FMaxFreeRatio:=8; // 1:1
|
|
end;
|
|
|
|
destructor TAVLTreeNodeMemManager.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TAVLTreeNodeMemManager.DisposeNode(ANode: TAVLTreeNode);
|
|
begin
|
|
if ANode=nil then exit;
|
|
if (FFreeCount<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
|
|
begin
|
|
// add ANode to Free list
|
|
ANode.Clear;
|
|
ANode.Right:=FFirstFree;
|
|
FFirstFree:=ANode;
|
|
inc(FFreeCount);
|
|
end else begin
|
|
// free list full -> free the ANode
|
|
ANode.Free;
|
|
end;
|
|
dec(FCount);
|
|
end;
|
|
|
|
function TAVLTreeNodeMemManager.NewNode: TAVLTreeNode;
|
|
begin
|
|
if FFirstFree<>nil then begin
|
|
// take from free list
|
|
Result:=FFirstFree;
|
|
FFirstFree:=FFirstFree.Right;
|
|
Result.Right:=nil;
|
|
end else begin
|
|
// free list empty -> create new node
|
|
Result:=TAVLTreeNode.Create;
|
|
end;
|
|
inc(FCount);
|
|
end;
|
|
|
|
procedure TAVLTreeNodeMemManager.Clear;
|
|
var ANode: TAVLTreeNode;
|
|
begin
|
|
while FFirstFree<>nil do begin
|
|
ANode:=FFirstFree;
|
|
FFirstFree:=FFirstFree.Right;
|
|
ANode.Right:=nil;
|
|
ANode.Free;
|
|
end;
|
|
FFreeCount:=0;
|
|
end;
|
|
|
|
procedure TAVLTreeNodeMemManager.SetMaxFreeRatio(NewValue: integer);
|
|
begin
|
|
if NewValue<0 then NewValue:=0;
|
|
if NewValue=FMaxFreeRatio then exit;
|
|
FMaxFreeRatio:=NewValue;
|
|
end;
|
|
|
|
procedure TAVLTreeNodeMemManager.SetMinFree(NewValue: integer);
|
|
begin
|
|
if NewValue<0 then NewValue:=0;
|
|
if NewValue=FMinFree then exit;
|
|
FMinFree:=NewValue;
|
|
end;
|
|
|
|
|
|
initialization
|
|
|
|
NodeMemManager:=TAVLTreeNodeMemManager.Create;
|
|
|
|
finalization
|
|
|
|
NodeMemManager.Free;
|
|
NodeMemManager:=nil;
|
|
|
|
end.
|
|
|