📄 sttree.pas
字号:
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower SysTools
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1996-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
{*********************************************************}
{* SysTools: StTree.pas 4.03 *}
{*********************************************************}
{* SysTools: AVL Tree class *}
{*********************************************************}
{$I StDefine.inc}
{Notes:
- These binary trees are self-balancing in the AVL sense (the depth
of any left branch differs by no more than one from the depth of the
right branch).
- Duplicate data is not allowed in a tree.
- Nodes can be of type TStTreeNode or any descendant.
- The Compare property of the TStContainer ancestor must be set to
specify the sort order of the tree. The Compare function operates
on Data pointers. The Data pointer could be typecast to a number
(any integer type), to a string pointer, to a record pointer, or to
an instance of a class.
- Next and Prev should not be used to iterate through an entire tree.
This is much slower than calling the Iterate method.
}
unit StTree;
interface
uses
Windows,
SysUtils, Classes, StConst, StBase;
type
TStTreeNode = class(TStNode)
{.Z+}
protected
tnPos : array[Boolean] of TStTreeNode; {Child nodes}
tnBal : Integer; {Used during balancing}
{.Z-}
public
constructor Create(AData : Pointer); override;
{-Initialize node}
end;
TStTree = class(TStContainer)
{.Z+}
protected
trRoot : TStTreeNode; {Root of tree}
trIgnoreDups : Boolean; {Ignore duplicates during Join?}
procedure ForEachPointer(Action : TIteratePointerFunc; OtherData : pointer);
override;
function StoresPointers : boolean;
override;
procedure trInsertNode(N : TStTreeNode);
{.Z-}
public
constructor Create(NodeClass : TStNodeClass); virtual;
{-Initialize an empty tree}
procedure LoadFromStream(S : TStream); override;
{-Create a list and its data from a stream}
procedure StoreToStream(S : TStream); override;
{-Write a list and its data to a stream}
procedure Clear; override;
{-Remove all nodes from container but leave it instantiated}
function Insert(Data : Pointer) : TStTreeNode;
{-Add a new node}
procedure Delete(Data : Pointer);
{-Delete a node}
function Find(Data : Pointer) : TStTreeNode;
{-Return node that matches Data}
procedure Assign(Source: TPersistent); override;
{-Assign another container's contents to this one}
procedure Join(T: TStTree; IgnoreDups : Boolean);
{-Add tree T into this one and dispose T}
function Split(Data : Pointer) : TStTree;
{-Split tree, putting all nodes above and including Data into new tree}
function Iterate(Action : TIterateFunc; Up : Boolean;
OtherData : Pointer) : TStTreeNode;
{-Call Action for all the nodes, returning the last node visited}
function First : TStTreeNode;
{-Return the smallest-value node in the tree}
function Last : TStTreeNode;
{-Return the largest-value node in the tree}
function Next(N : TStTreeNode) : TStTreeNode;
{-Return the next node whose value is larger than N's}
function Prev(N : TStTreeNode) : TStTreeNode;
{-Return the largest node whose value is smaller than N's}
end;
{.Z+}
TStTreeClass = class of TStTree;
{.Z-}
{======================================================================}
implementation
{$IFDEF ThreadSafe}
var
ClassCritSect : TRTLCriticalSection;
{$ENDIF}
procedure EnterClassCS;
begin
{$IFDEF ThreadSafe}
EnterCriticalSection(ClassCritSect);
{$ENDIF}
end;
procedure LeaveClassCS;
begin
{$IFDEF ThreadSafe}
LeaveCriticalSection(ClassCritSect);
{$ENDIF}
end;
const
Left = False;
Right = True;
{Following stack declarations are used to avoid recursion in all tree
routines. Because the tree is AVL-balanced, a stack size of 40
allows at least 2**32 elements in the tree without overflowing the
stack.}
const
StackSize = 40;
type
StackNode =
record
Node : TStTreeNode;
Comparison : Integer;
end;
StackArray = array[1..StackSize] of StackNode;
constructor TStTreeNode.Create(AData : Pointer);
begin
inherited Create(AData);
end;
{----------------------------------------------------------------------}
function Sign(I : Integer) : Integer;
begin
if I < 0 then
Sign := -1
else if I > 0 then
Sign := +1
else
Sign := 0;
end;
procedure DelBalance(var P : TStTreeNode; var SubTreeDec : Boolean; CmpRes : Integer);
var
P1, P2 : TStTreeNode;
B1, B2 : Integer;
LR : Boolean;
begin
CmpRes := Sign(CmpRes);
if P.tnBal = CmpRes then
P.tnBal := 0
else if P.tnBal = 0 then begin
P.tnBal := -CmpRes;
SubTreeDec := False;
end else begin
LR := (CmpRes < 0);
P1 := P.tnPos[LR];
B1 := P1.tnBal;
if (B1 = 0) or (B1 = -CmpRes) then begin
{Single RR or LL rotation}
P.tnPos[LR] := P1.tnPos[not LR];
P1.tnPos[not LR] := P;
if B1 = 0 then begin
P.tnBal := -CmpRes;
P1.tnBal := CmpRes;
SubTreeDec := False;
end else begin
P.tnBal := 0;
P1.tnBal := 0;
end;
P := P1;
end else begin
{Double RL or LR rotation}
P2 := P1.tnPos[not LR];
B2 := P2.tnBal;
P1.tnPos[not LR] := P2.tnPos[LR];
P2.tnPos[LR] := P1;
P.tnPos[LR] := P2.tnPos[not LR];
P2.tnPos[not LR] := P;
if B2 = -CmpRes then
P.tnBal := CmpRes
else
P.tnBal := 0;
if B2 = CmpRes then
P1.tnBal := -CmpRes
else
P1.tnBal := 0;
P := P2;
P2.tnBal := 0;
end;
end;
end;
procedure InsBalance(var P : TStTreeNode; var SubTreeInc : Boolean;
CmpRes : Integer);
var
P1 : TStTreeNode;
P2 : TStTreeNode;
LR : Boolean;
begin
CmpRes := Sign(CmpRes);
if P.tnBal = -CmpRes then begin
P.tnBal := 0;
SubTreeInc := False;
end else if P.tnBal = 0 then
P.tnBal := CmpRes
else begin
LR := (CmpRes > 0);
P1 := P.tnPos[LR];
if P1.tnBal = CmpRes then begin
P.tnPos[LR] := P1.tnPos[not LR];
P1.tnPos[not LR] := P;
P.tnBal := 0;
P := P1;
end else begin
P2 := P1.tnPos[not LR];
P1.tnPos[not LR] := P2.tnPos[LR];
P2.tnPos[LR] := P1;
P.tnPos[LR] := P2.tnPos[not LR];
P2.tnPos[not LR] := P;
if P2.tnBal = CmpRes then
P.tnBal := -CmpRes
else
P.tnBal := 0;
if P2.tnBal = -CmpRes then
P1.tnBal := CmpRes
else
P1.tnBal := 0;
P := P2;
end;
P.tnBal := 0;
SubTreeInc := False;
end;
end;
function JoinNode(Container : TStContainer; Node : TStNode;
OtherData : Pointer) : Boolean; far;
var
N : TStTreeNode;
begin
Result := True;
N := TStTree(OtherData).Find(Node.Data);
if Assigned(N) then
if TStTree(OtherData).trIgnoreDups then begin
Node.Free;
Exit;
end else
RaiseContainerError(stscDupNode);
with TStTreeNode(Node) do begin
tnPos[Left] := nil;
tnPos[Right] := nil;
tnBal := 0;
end;
TStTree(OtherData).trInsertNode(TStTreeNode(Node));
end;
type
SplitRec =
record
SData : Pointer;
STree : TStTree;
end;
function SplitTree(Container : TStContainer; Node : TStNode;
OtherData : Pointer) : Boolean; far;
var
D : Pointer;
begin
Result := True;
if Container.DoCompare(Node.Data, SplitRec(OtherData^).SData) >= 0 then begin
D := Node.Data;
TStTree(Container).Delete(D);
SplitRec(OtherData^).STree.Insert(D);
end;
end;
type
TStoreInfo = record
Wtr : TWriter;
SDP : TStoreDataProc;
end;
function StoreNode(Container : TStContainer; Node : TStNode;
OtherData : Pointer) : Boolean; far;
begin
Result := True;
with TStoreInfo(OtherData^) do
SDP(Wtr, Node.Data);
end;
function AssignData(Container : TStContainer;
Data, OtherData : Pointer) : Boolean; far;
var
OurTree : TStTree absolute OtherData;
begin
OurTree.Insert(Data);
Result := true;
end;
{----------------------------------------------------------------------}
procedure TStTree.Assign(Source: TPersistent);
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
{The only containers that we allow to be assigned to a tree are
- a SysTools linked list (TStList)
- another SysTools binary search tree (TStTree)
- a SysTools collection (TStCollection, TStSortedCollection)}
if not AssignPointers(Source, AssignData) then
inherited Assign(Source);
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;{try..finally}
{$ENDIF}
end;
procedure TStTree.Clear;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
if conNodeProt = 0 then
Iterate(DestroyNode, True, nil);
trRoot := nil;
FCount := 0;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
procedure TStTree.ForEachPointer(Action : TIteratePointerFunc;
OtherData : pointer);
var
P : TStTreeNode;
Q : TStTreeNode;
StackP : Integer;
Stack : StackArray;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
StackP := 0;
P := trRoot;
repeat
while Assigned(P) do begin
Inc(StackP);
Stack[StackP].Node := P;
P := P.tnPos[false];
end;
if StackP = 0 then begin
Exit;
end;
P := Stack[StackP].Node;
Dec(StackP);
Q := P;
P := P.tnPos[true];
if not Action(Self, Q.Data, OtherData) then begin
Exit;
end;
until False;
{$IFDEF ThreadSafe}
finally
LeaveCS;
end;
{$ENDIF}
end;
function TStTree.StoresPointers : boolean;
begin
Result := true;
end;
constructor TStTree.Create(NodeClass : TStNodeClass);
begin
CreateContainer(NodeClass, 0);
end;
procedure TStTree.Delete(Data : Pointer);
var
P : TStTreeNode;
Q : TStTreeNode;
TmpData : Pointer;
CmpRes : Integer;
Found : Boolean;
SubTreeDec : Boolean;
StackP : Integer;
Stack : StackArray;
begin
{$IFDEF ThreadSafe}
EnterCS;
try
{$ENDIF}
P := trRoot;
if not Assigned(P) then
Exit;
{Find node to delete and stack the nodes to reach it}
Found := False;
StackP := 0;
while not Found do begin
CmpRes := DoCompare(Data, P.Data);
Inc(StackP);
if CmpRes = 0 then begin
{Found node to delete}
with Stack[StackP] do begin
Node := P;
Comparison := -1;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -