|
#
# Second AVL Tree Implementations
#
# The plan is that these will use bitfields to efficiently implement threading and keep track of subtree size
# so that we can have a fast enumerator. Because of the threading they will not be extensions of BSTs
#
#
#
# The nodes of our Tree are plain lists of length 4. Entry 1 is the left child, or predecessor,
# entry 2 the data and entry 3 the right child or successor
# entry 4 has four bitfields: imbalance (2 bits), has_leftchild (1), has_rightchild (2) and size (the rest)
# The root of the tree is held in a length 1 plain list, which is then held in a component object.
#
#
# REcord for "local" things
#
AVL := rec();
DeclareRepresentation("IsAVLTreeRep", IsComponentObjectRep, []);
AVL.DefaultType := NewType(OrderedSetDSFamily, IsAVLTreeRep and IsOrderedSetDS and IsMutable);
AVL.DefaultTypeStandard := NewType(OrderedSetDSFamily, IsAVLTreeRep and IsStandardOrderedSetDS and IsMutable);
AVL.nullIterator := Iterator([]);
AVL.Bitfields := MakeBitfields(2,1,1,GAPInfo.BytesPerVariable*8-8);
AVL.getImbalance := AVL.Bitfields.getters[1];
AVL.setImbalance := AVL.Bitfields.setters[1];
AVL.hasLeft := AVL.Bitfields.booleanGetters[2];
AVL.setHasLeft := AVL.Bitfields.booleanSetters[2];
AVL.hasRight := AVL.Bitfields.booleanGetters[3];
AVL.setHasRight := AVL.Bitfields.booleanSetters[3];
AVL.getSubtreeSize := AVL.Bitfields.getters[4];
AVL.setSubtreeSize := AVL.Bitfields.setters[4];
#
# Worker function for construction -- relies on the fact that we can make a perfectly balanced BST in linear time
# from a sorted list of objects.
#
AVL.NewTree :=
function(isLess, set)
local type;
if not IsSet(set) or isLess <> \< then
if not IsMutable(set) then
set := ShallowCopy(set);
fi;
Sort(set, isLess);
fi;
if isLess = \< then
type := AVL.DefaultTypeStandard;
else
type := AVL.DefaultType;
fi;
return Objectify( type, rec(
lists := AVL.TreeByOrderedList(set),
isLess := isLess));
end;
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsOrderedSetDS and IsMutable, IsFunction],
function(type, isLess)
return AVL.NewTree(isLess, []);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsOrderedSetDS and IsMutable],
function(type)
return AVL.NewTree(\<, []);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsFunction, IsRandomSource],
function(type, isLess, rs)
return AVL.NewTree(isLess, []);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsListOrCollection],
function(type, data)
return AVL.NewTree(\<, data);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsListOrCollection, IsRandomSource],
function(type, data, rs)
return AVL.NewTree(\<, data);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsOrderedSetDS],
function(type, os)
return AVL.NewTree(\<, AsSortedList(os));
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection],
function(type, isLess, data)
return AVL.NewTree(isLess, data);
end);
#
# If we're given an iterator, worth the time to drain and sort
#
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsIterator],
function(type, iter)
local l, x;
l := [];
for x in iter do
Add(l,x);
od;
return AVL.NewTree(\<, l);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator],
function(type, isLess, iter)
local l, x;
l := [];
for x in iter do
Add(l,x);
od;
return AVL.NewTree(isLess, l);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsFunction, IsListOrCollection, IsRandomSource],
function(type, isLess, data, rs)
return AVL.NewTree(isLess, data);
end);
InstallMethod(OrderedSetDS, [IsAVLTreeRep and IsMutable and IsOrderedSetDS, IsFunction, IsIterator, IsRandomSource],
function(type, isLess, iter, rs)
local l, x;
l := [];
for x in iter do
Add(l,x);
od;
return AVL.NewTree(isLess, l);
end);
#
# A worker function used when we are searching the tree, but NOT modifying it
# C version to follow. Returns the node containing val, or fail.
#
AVL.FindGAP := function(tree, val, less)
local ghl, ghr, node, d, flags;
#
# node is the current node
#
ghl := AVL.hasLeft;
ghr := AVL.hasRight;
if not IsBound(tree[1]) then
return fail;
fi;
node := tree[1];
while true do
d := node[2];
#
# Work out which way to go next
#
if val = d then
return node;
fi;
flags := node[4];
if less(val,d) then
if ghl(flags) then
node := node[1];
else
return fail;
fi;
elif ghr(flags) then
node := node[3];
else
return fail;
fi;
od;
end;
#
# Prefer the C version
#
if IsBound(DS_AVL_FIND) then
AVL.Find := DS_AVL_FIND;
else
AVL.Find := AVL.FindGAP;
fi;
#
# With this worker function \in is really easy
#
InstallMethod(\in, [IsObject, IsAVLTreeRep and IsOrderedSetDS],
function(val, tree)
return fail <> AVL.Find(tree!.lists, val, tree!.isLess);
end);
#
# Some classic utility functions
#
AVL.Height := function(tree)
local avlh;
if not IsBound(tree!.lists[1]) then
return 0;
fi;
avlh := function(node)
local flags, hl, hr;
flags := node[4];
if AVL.hasLeft(flags) then
hl := avlh(node[1]);
else
hl := 0;
fi;
if AVL.hasRight(flags) then
hr := avlh(node[3]);
else
hr := 0;
fi;
return 1 + Maximum(hl,hr);
end;
return avlh(tree!.lists[1]);
end;
AVL.CheckSize := function(node)
local flags, sl, sr, s;
flags := node[4];
if AVL.hasLeft(flags) then
sl := AVL.CheckSize(node[1]);
else
sl := 0;
fi;
if AVL.hasRight(flags) then
sr := AVL.CheckSize(node[3]);
else
sr := 0;
fi;
if sl = false or sr = false then
return false;
fi;
s := 1+sl+sr;
if s <> AVL.getSubtreeSize(flags) then
return false;
else
return s;
fi;
end;
InstallMethod(Size, [IsAVLTreeRep and IsOrderedSetDS],
function(tree)
if not IsBound(tree!.lists[1]) then
return 0;
else
return AVL.getSubtreeSize(tree!.lists[1][4]);
fi;
end);
#
# Used in the constructors
# build tree directly by divide and conquer
#
AVL.TreeByOrderedList := function(l)
local foo,x;
foo := function(l, from, to)
local mid, left, right, node, hl, hasl, min, hr, hasr, max;
#
# returns a list
# [height of subtree, root node, min node, max node]
# representing the section of l from from to to
# inclusive or fail if from > to
#
if from > to then
return fail;
fi;
mid := QuoInt(from + to,2);
left := foo(l,from, mid-1);
right := foo(l, mid+1, to);
node := [,l[mid],,];
#
# We have a left subtree, note it and link it's rightmost
# nodes thread pointer to the current node
#
if left <> fail then
node[1] := left[2];
hl := left[1];
hasl := 1;
left[4][3] := node;
min := left[3];
else
hasl := 0;
hl := 0;
min := node;
fi;
#
# and the same on the other side
#
if right <> fail then
node[3] := right[2];
hr := right[1];
hasr := 1;
right[3][1] := node;
max := right[4];
else
hasr := 0;
hr := 0;
max := node;
fi;
#
# Now assemble the bitfield with all the miscellaneous data
#
node[4] := BuildBitfields(AVL.Bitfields.widths, hr-hl+1, hasl, hasr, to - from + 1);
return [Maximum(hl,hr)+1, node, min, max];
end;
x := foo(l, 1, Length(l));
#
# wrap result up nicely
#
if x = fail then
return [];
else
return [x[2]];
fi;
end;
AVL.MinimalNode := function(rootnode)
local x, ghl;
x := rootnode;
ghl := AVL.hasLeft;
while ghl(x[4]) do
x := x[1];
od;
return x;
end;
AVL.MaximalNode := function(rootnode)
local x, ghr;
x := rootnode;
ghr := AVL.hasRight;
while ghr(x[4]) do
x := x[3];
od;
return x;
end;
AVL.MakeIterator :=
function(tree)
if not IsBound(tree!.lists[1]) then
return AVL.nullIterator;
fi;
return IteratorByFunctions(rec(
node := AVL.MinimalNode(tree!.lists[1]),
IsDoneIterator := iter -> iter!.node = fail,
NextIterator := function(iter)
local toreturn, node;
toreturn := iter!.node[2];
if AVL.hasRight(iter!.node[4]) then
iter!.node := AVL.MinimalNode(iter!.node[3]);
elif IsBound(iter!.node[3]) then
iter!.node := iter!.node[3];
else
iter!.node := fail;
fi;
return toreturn;
end,
ShallowCopy := function(iter)
return rec(node := iter!.node,
IsDoneIterator := iter!.IsDoneIterator,
NextIterator := iter!.NextIterator,
ShallowCopy := iter!.ShallowCopy,
PrintObj := iter!.PrintObj);
end,
PrintObj := function(iter)
Print("<Iterator of AVL tree>");
end ));
end;
InstallMethod(Iterator, [IsAVLTreeRep and IsOrderedSetDS],
AVL.MakeIterator);
InstallMethod(IteratorSorted, [IsStandardOrderedSetDS and IsAVLTreeRep],
AVL.MakeIterator);
InstallMethod(ViewString, [IsAVLTreeRep and IsOrderedSetDS],
t -> Concatenation("<avl tree size ",String(Size(t)),">"));
InstallMethod(AsList, [IsAVLTreeRep and IsOrderedSetDS],
function(tree)
if not IsMutable(tree) then
return tree;
fi;
TryNextMethod();
end);
InstallMethod(AsSSortedList, [IsAVLTreeRep and IsStandardOrderedSetDS],
function(tree)
if not IsMutable(tree) then
return tree;
fi;
TryNextMethod();
end);
#
# We copy the tree, but not the data.
#
# Threading makes this harder -- maybe flattened and rebuild
# means that the copy is not the same shape as the original
#
#
InstallMethod(ShallowCopy, [IsAVLTreeRep and IsOrderedSetDS],
tree -> OrderedSetDS(IsAVLTreeRep, tree));
#
# This is more of less the same method as for Skiplists -- unify?
#
InstallMethod(String, [IsAVLTreeRep and IsOrderedSetDS],
function(avl)
local s, isLess;
s := [];
Add(s,"OrderedSetDS(IsAVLTreeRep");
isLess := avl!.isLess;
if isLess <> \< then
Add(s,", ");
Add(s,String(isLess));
fi;
if not IsEmpty(avl) then
Add(s, ", ");
Add(s, String(AsList(avl)));
fi;
Add(s,")");
return Concatenation(s);
end);
InstallMethod(LessFunction, [IsAVLTreeRep and IsOrderedSetDS],
tree -> tree!.isLess);
#
# Now we get into worker routines. There are lots of these, because the algorithms are complex
# Some of these will get C implementations, others are there to be passed to the C implementations so that they
# can call back conveniently
#
#
# Trinode restructuring
# This is called when the node avl has just become unbalanced because one of its subtrees
# has become higher after an insertion. We consider three nodes, avl, its taller child and its taller child
# (avls tallest grandchild)
# We reorganise these three so that the middle one in the ordering is at the top with the other two as its children and
# the remaining subtrees attached in the only way they can be.
# There are two cases, depending on whether the grandchild is on the same side of the child as the child is of avl, or not
#
# Try and write a single trinode function that will do insert and delete
#
AVL.Trinode := function(avl)
local gim, sim, gs, ss, htchange, aflags, dirn, i, j, ghi, shi,
ghj, shj, y, yflags, im, sa, sb, sc, z, zflags, iz, sd;
gim := AVL.getImbalance;
sim := AVL.setImbalance;
gs := AVL.getSubtreeSize;
ss := AVL.setSubtreeSize;
#
# We restructure on the taller side
# i points that way, j the opposite
# Return value is a length 2 list [<change in height>,<new top node>]
#
htchange := -1;
aflags := avl[4];
dirn := gim(aflags);
if dirn = 0 then
i := 1;
j := 3;
ghi := AVL.hasLeft;
shi := AVL.setHasLeft;
ghj := AVL.hasRight;
shj := AVL.setHasRight;
else
i := 3;
j := 1;
ghi := AVL.hasRight;
shi := AVL.setHasRight;
ghj := AVL.hasLeft;
shj := AVL.setHasLeft;
fi;
y := avl[i];
yflags := y[4];
if gim(yflags) <> 2-dirn then
#
# Same sided case, so the child y is the middle one of the three nodes
#
#
# Transfer y's smaller child to avl in place of y
# and make avl a child of y
#
if ghj(yflags) then
avl[i] := y[j];
y[j] := avl;
else
#
# No child, so set thread pointer
# In this case y[j] is already avl, but we need
# to change the bit that determines the meaning of that field
#
avl[i] := y;
aflags := shi(aflags,false);
yflags := shj(yflags,true);
fi;
#
# adjust imbalances
#
#
# depends on whether y was balanced before
#
im := gim(yflags);
if im = 1 then
#
# This can only happen while deleting
#
aflags := sim(aflags,dirn);
yflags := sim(yflags,2-dirn);
htchange := 0;
else
aflags := sim(aflags,1);
yflags := sim(yflags,1);
fi;
#
# adjust sizes
#
# get the sizes of the 3 subtrees below avl and y
#
if ghj(aflags) then
sa := gs(avl[j][4]);
else
sa := 0;
fi;
if ghi(aflags) then
sb := gs(avl[i][4]);
else
sb := 0;
fi;
if ghi(yflags) then
sc := gs(y[i][4]);
else
sc := 0;
fi;
y[4] := ss(yflags,sa+sb+sc+2);
avl[4] := ss(aflags,sa+sb+1);
#
# return value is the new top node
#
return [htchange,y];
else
#
# The other case, y is the child and z, the grandchild is the middle one of the three
#
z := y[j];
zflags := z[4];
#
# z is coming to the top, so we need to rehome both its children
#
if ghi(zflags) then
y[j] := z[i];
else
#
# link from y continues to point at z but changes meaning
#
yflags := shj(yflags,false);
fi;
if ghj(zflags) then
avl[i] := z[j];
else
#
# link from avl points back up to z
#
avl[i] := z;
aflags := shi(aflags,false);
fi;
#
# Now we make z the new top node with y and avl as its children
#
z[i] := y;
z[j] := avl;
zflags := shi(shj(zflags,true),true);
#
# The new imbalances of y and avl depend on the old imbalance of z
#
iz := gim(zflags);
if iz = dirn then
yflags := sim(yflags, 1);
aflags := sim(aflags,2-dirn);
elif iz = 1 then
yflags := sim(yflags,1);
aflags := sim(aflags,1);
else
yflags := sim(yflags,dirn);
aflags := sim(aflags,1);
fi;
#
# which always ends up balanced
#
zflags := sim(zflags,1);
#
# Finally we need to set all the sizes
#
if ghj(aflags) then
sa := gs(avl[j][4]);
else
sa := 0;
fi;
if ghi(aflags) then
sb := gs(avl[i][4]);
else
sb := 0;
fi;
if ghj(yflags) then
sc := gs(y[j][4]);
else
sc := 0;
fi;
if ghi(yflags) then
sd := gs(y[i][4]);
else
sd := 0;
fi;
avl[4] := ss(aflags,sa+sb+1);
y[4] := ss(yflags,sc+sd+1);
z[4] := ss(zflags, sa+sb+sc+sd+3);
fi;
return [-1,z];
end;
#
# Here is the routine we actually plan to move into C
# returns fail if val was already present
# 0 if the subtree ends up the same depth and with the same root
# 1 if the root is the same but the subtree got deeper
# the new root if the root changed (due to a trinode restructuring
# in which case the tree did not get deeper
#
AVL.AddSetInnerGAP :=
function(avl, val, less, trinode)
local d, i, j, ghi, shi, dirn, gim, sim, gs, ss, newnode, im,
deeper;
#
# This recursive routine inserts val into the relevant subtree of avl
# returns fail if val was already present
# 0 if the subtree ends up the same depth and with the same root
# 1 if the root is the same but the subtree got deeper
# the new root if the root changed (due to a trinode restructuring
# in which case the tree did not get deeper
#
#
# Work out which subtree to look in
#
d := avl[2];
if val = d then
return fail;
elif less(val, d) then
i := 1;
j := 3;
ghi := AVL.hasLeft;
shi := AVL.setHasLeft;
dirn := 0;
else
dirn := 2;
i := 3;
j := 1;
ghi := AVL.hasRight;
shi := AVL.setHasRight;
fi;
gim := AVL.getImbalance;
sim := AVL.setImbalance;
gs := AVL.getSubtreeSize;
ss := AVL.setSubtreeSize;
if not ghi(avl[4]) then
# inserting a new leaf here
newnode := [,val,,BuildBitfields(AVL.Bitfields.widths,1,0,0,1)];
newnode[j] := avl;
if IsBound(avl[i]) then
newnode[i] := avl[i];
fi;
avl[i] := newnode;
avl[4] := ss(shi(avl[4], true),gs(avl[4])+1);
# we have tilted over, but can't have become unbalanced by more than 1
im := gim(avl[4])+dirn-1;
avl[4] := sim(avl[4],im);
# if we are now unbalanced by 1 then the tree got deeper
return AbsInt(im-1);
else
#
# recurse into the subtree
#
deeper := AVL.AddSetInner(avl[i],val,less, trinode);
#
# nothing more to do
#
if deeper = 0 then
avl[4] := ss(avl[4],gs(avl[4])+1);
return 0;
elif deeper = fail then
return fail;
elif deeper = 1 then
#
# the subtree got deeper, so we need to adjust imbalance and maybe restructure
#
im := gim(avl[4]);
if im <> dirn then
# we can do it by adjusting imbalance
im := im+dirn-1;
# also update size
avl[4] := ss(sim(avl[4],im),gs(avl[4])+1);
return AbsInt(im-1);
else
#
# or we can't.
#
return trinode(avl)[2];
fi;
else
#
# restructure happened just beneath our feet. Deal with it and return
#
avl[i] := deeper;
avl[4] := ss(avl[4],gs(avl[4])+1);
return 0;
fi;
fi;
end;
if IsBound(DS_AVL_ADDSET_INNER) then
AVL.AddSetInner := DS_AVL_ADDSET_INNER;
else
AVL.AddSetInner := AVL.AddSetInnerGAP;
fi;
#
# Just bookkeeping here.
#
InstallMethod(AddSet, [IsAVLTreeRep and IsOrderedSetDS and IsMutable, IsObject],
function(avl, val)
local res;
if not IsBound(avl!.lists[1]) then
avl!.lists[1] := [,val,,BuildBitfields(AVL.Bitfields.widths,1,0,0,1)];
return 1;
fi;
res := AVL.AddSetInner(avl!.lists[1],val,avl!.isLess, AVL.Trinode);
if res = fail then
return 0;
fi;
if not IsInt(res) then
avl!.lists[1] := res;
fi;
return 1;
end);
InstallMethod(DisplayString, [IsAVLTreeRep],
function(tree)
local nodestring, w, layer, s, newlayer, llen, node, ns;
if not IsBound(tree!.lists[1]) then
return "<empty tree>";
fi;
nodestring := function(node)
local s;
s := ["<",ViewString(node[2]),": ",String(AVL.getSubtreeSize(node[4])), " ",
["l","b","r"][AVL.getImbalance(node[4])+1]," "];
if not IsBound(node[1]) then
Add(s, ". ");
elif AVL.hasLeft(node[4]) then
Append(s, ["<",ViewString(node[1][2]),"> "]);
else
Append(s, ["(",ViewString(node[1][2]),") "]);
fi;
if not IsBound(node[3]) then
Add(s, ".");
elif AVL.hasRight(node[4]) then
Append(s, ["<",ViewString(node[3][2]),">"]);
else
Append(s, ["(",ViewString(node[3][2]),")"]);
fi;
Add(s,">");
return Concatenation(s);
end;
w := SizeScreen()[1];
layer := [tree!.lists[1]];
s := [];
while Length(layer) > 0 do
newlayer := [];
Add(s,"\>\>");
llen := 0;
for node in layer do
ns := nodestring(node);
if llen + Length(ns)+1 >= w then
Add(s,"\n");
llen := 2;
fi;
Add(s,ns);
Add(s," ");
llen := llen + Length(ns)+1;
if AVL.hasLeft(node[4]) then
Add(newlayer, node[1]);
fi;
if AVL.hasRight(node[4]) then
Add(newlayer, node[3]);
fi;
od;
Add(s,"\<\<\n");
layer := newlayer;
od;
return Concatenation(s);
end);
InstallMethod(Length, [IsAVLTreeRep and IsOrderedSetDS], Size);
InstallMethod(ELM_LIST, [IsAVLTreeRep and IsOrderedSetDS, IsPosInt],
function(tree,n)
local getNth, node;
getNth := function(node,n)
local sl;
if not AVL.hasLeft(node[4]) then
sl := 0;
else
sl := AVL.getSubtreeSize(node[1][4]);
fi;
if sl >= n then
return getNth(node[1],n);
elif n = sl+1 then
return node[2];
else
Assert(2, AVL.hasRight(node[4]));
return getNth(node[3],n - sl -1);
fi;
end;
node := tree!.lists[1];
if AVL.getSubtreeSize(node[4]) < n then
Error("No entry at position ",n);
fi;
return getNth(node,n);
end);
#
# This function returns the position of val in tree or the negative of the position
# it would have, were it to be inserted. It is used for methods for Position and
# related operations
#
AVL.PositionInner := function(tree, val)
local posInner;
if IsEmpty(tree) then
return -1;
fi;
posInner := function(node, offset, val)
local sl, d;
if AVL.hasLeft(node[4]) then
sl := AVL.getSubtreeSize(node[1][4]);
else
sl := 0;
fi;
d := node[2];
if d = val then
return offset+sl+1;
elif LessFunction(tree)(val,d) then
if sl = 0 then
return -offset-1;
else
return posInner(node[1],offset,val);
fi;
else
if AVL.hasRight(node[4]) then
return posInner(node[3],offset+sl+1,val);
else
return -offset-sl-2;
fi;
fi;
end;
return posInner(tree!.lists[1], 0, val);
end;
InstallMethod(Position, [IsAVLTreeRep and IsOrderedSetDS, IsObject, IsInt],
function( tree, val, start)
local p;
p := AVL.PositionInner(tree, val);
if p < start then
return fail;
fi;
return p;
end);
InstallMethod(PositionSortedOp, [IsAVLTreeRep and IsStandardOrderedSetDS, IsObject],
function( tree, val)
return AbsInt(AVL.PositionInner(tree, val));
end);
InstallMethod(PositionSortedOp, [IsAVLTreeRep and IsOrderedSetDS, IsObject, IsFunction],
function( tree, val, comp)
if comp <> LessFunction(tree) then
TryNextMethod();
fi;
return AbsInt(AVL.PositionInner(tree, val));
end);
# This routine handles removal of the predecessor or successor of the data item at node l
# This is needed, just as for BSTs when l is to be deleted but has two children
# It's more complicated than for BSTs because we need to rebalance and do bookkeeping as we come up
#
AVL.Remove_Extremal := function(l, dirn)
local i, j, hi, hj, flags, k, res, newext, im, res2;
#
# This removes the dirn-most node of the tree rooted at l.
# it returns a 4-tuple [<change in height>, <node removed>, <new root node>, <new extremal node>]
# if in fact it deletes the only node in the subtree below l, it returns fail
# in the third component and no fourth component
#
if dirn = 0 then
i := 1;
j := 3;
hi := AVL.hasLeft;
hj := AVL.hasRight;
else
i := 3;
j := 1;
hj := AVL.hasLeft;
hi := AVL.hasRight;
fi;
flags := l[4];
if not hi(flags) then
#
# Found it
#
#
if not hj(flags) then
#
# Node we are removing is a leaf.
# So no thread pointers point to it
#
return [-1, l, fail];
else
#
# Node we are removing has a child, so one thread pointer points to it
# We have to find the node containing that pointer and return it, so the
# calling routine can adjust that thread pointer
#
k := l[j];
while hi(k[4]) do
k := k[i];
od;
return [-1, l, l[j], k];
fi;
fi;
#
# recurse
#
res := AVL.Remove_Extremal(l[i],dirn);
if res[3] <> fail then
#
# There's still a subtree below us, so attach it.
#
l[i] := res[3];
newext := res[4];
else
#
# We just deleted the only node in our i-subtree
# so the i child is replaced by a thread pointer
# the node we deleted must have been the i-most node, so will
# have had a thread pointer in place of its i child which
# tells us where to link to
#
l[i] := res[2][i];
if dirn = 0 then
flags := AVL.setHasLeft(flags, false);
else
flags := AVL.setHasRight(flags, false);
fi;
#
# In this case we are the new extremal node, and our j
#
newext := l;
fi;
#
# Adjust size
#
flags := AVL.setSubtreeSize(flags, AVL.getSubtreeSize(flags)-1);
#
# if the subtree got shorter then adjust balance
#
if res[1] = -1 then
im := AVL.getImbalance(flags);
if im = dirn then
l[4] := AVL.setImbalance(flags,1);
return [-1, res[2], l, newext];
elif im = 1 then
l[4] := AVL.setImbalance(flags,2-dirn);
return [0, res[2], l, newext];
else
l[4] := flags;
res2 := AVL.Trinode(l);
return [res2[1],res[2],res2[2],newext];
fi;
else
l[4] := flags;
return [0, res[2],l, newext];
fi;
end;
#
# This function captures the work that must be done when we have found the node we want to delete
#
AVL.RemoveThisNode := function(node, remove_extremal, trinode)
local flags, im, res, l;
#
# Very similar to the BST case. By careful choices we avoid the need to
# restructure at this point, but we do need to do book-keeping
#
# returns change in height and new node
#
flags := node[4];
if AVL.hasLeft(flags) then
if AVL.hasRight(flags) then
#
# Both -- hard case
#
# We "steal" a neighbouring value from a subtree
# if they are of unequal height, choose the higher
# If equal go left. We don't need to alternate as we do
# for BSTs because these trees cannot become too unbalanced
#
im := AVL.getImbalance(flags);
if im = 2 then
res := remove_extremal(node[3],0);
#
# Since we have two children and we are working on the higher one, it
# cannot entirely vanish
#
Assert(2, res[3] <> fail);
node[3] := res[3];
res[4][1] := node;
else
res := remove_extremal(node[1],2);
if res[3] = fail then
#
# Child was a singleton node, which we have deleted, need
# to link up thread pointer
#
if IsBound(node[1][1]) then
node[1] := node[1][1];
else
Unbind(node[1]);
fi;
flags := AVL.setHasLeft(flags, false);
else
node[1] := res[3];
res[4][3] := node;
fi;
fi;
node[2] := res[2][2];
# Adjust balance
#
if res[1] <> 0 then
if im <> 1 then
#
# Not balanced before, so now we are
#
node[4] := AVL.setSubtreeSize(AVL.setImbalance(flags,1),AVL.getSubtreeSize(flags)-1);
return [-1,node];
else
#
# If we were balanced before, we went left
#
node[4] := AVL.setSubtreeSize(AVL.setImbalance(flags,2),AVL.getSubtreeSize(flags)-1); ;
return [0,node];
fi;
else
node[4] := AVL.setSubtreeSize(flags,AVL.getSubtreeSize(flags)-1); ;
return [0, node];
fi;
else
#
# left only -- in this case the left child must be a singleton node
# because of the balance condition
#
#
# Since we only have one child there is one link pointer that points to me
# so I need to find and fix it to point to my successor
#
l := node[1];
Assert(2, not AVL.hasLeft(l[4]) and not AVL.hasRight(l[4]));
Assert(2, IsIdenticalObj(l[3], node));
if IsBound(node[3]) then
l[3] := node[3];
else
Unbind(l[3]);
fi;
return [-1,l];
fi;
else
if AVL.hasRight(flags) then
#
# right only -- again the child must be a singleton
#
l := node[3];
Assert(2, not AVL.hasLeft(l[4]) and not AVL.hasRight(l[4]));
Assert(2, IsIdenticalObj(l[1], node));
if IsBound(node[1]) then
l[1] := node[1];
else
Unbind(l[1]);
fi;
return [-1, l];
else
#
# None
#
return [-1, fail];
fi;
fi;
end;
#
# Finally the time-critical recursion to find the node to delete and clean up on the way out
# This is a GAP reference implementation to the C version
#
AVL.RemoveSetInnerGAP := function(node,val, less, remove_extremal, trinode, remove_this)
local d, i, hi, shi, ret, flags, im;
#
# deletes val at or below this node
# returns a pair [<change in height>, <new node>]
#
d := node[2];
if val = d then
#
# Found it
#
return remove_this(node, remove_extremal, trinode);
fi;
if less(val, d) then
i := 1;
hi := AVL.hasLeft;
shi := AVL.setHasLeft;
else
i := 3;
hi := AVL.hasRight;
shi := AVL.setHasRight;
fi;
flags := node[4];
if hi(flags) then
ret := AVL.RemoveSetInner(node[i],val, less, remove_extremal, trinode, remove_this);
if ret = fail then
return fail;
fi;
if ret[2] <> fail then
node[i] := ret[2];
else
flags := shi(flags, false);
if IsBound(node[i][i]) then
node[i] := node[i][i];
else
Unbind(node[i]);
fi;
fi;
else
return fail;
fi;
#
# So if we get here we have deleted val somewhere below here, and replaced the subtree that might have been changed
# by rotations, and ret[1] tells us if that subtree got shorter. If it did, we may have more work to do
#
flags := AVL.setSubtreeSize(flags, AVL.getSubtreeSize(flags)-1);
#
# We reuse ret for the return from this function to avoid garbage
#
if ret[1] = 0 then
#
# No more to do
#
node[4] := flags;
ret[2] := node;
return ret;
fi;
#
# or maybe all we need to do is adjust the imbalance at this node
#
im := AVL.getImbalance(flags);
if im = i-1 then
node[4] := AVL.setImbalance(flags, 1);
ret[2] := node;
return ret;
elif im = 1 then
node[4] := AVL.setImbalance(flags, 3-i);
ret[1] := 0;
ret[2] := node;
return ret;
fi;
#
# Nope. Need to rebalance
#
node[4] := flags;
return trinode(node);
end;
if IsBound(DS_AVL_REMSET_INNER) then
AVL.RemoveSetInner := DS_AVL_REMSET_INNER;
else
AVL.RemoveSetInner := AVL.RemoveSetInnerGAP;
fi;
#
# This is now just a wrapper around the "Inner" function
#
InstallMethod(RemoveSet, [IsAVLTreeRep and IsOrderedSetDS and IsMutable, IsObject],
function(avl, val)
local ret;
if not IsBound(avl!.lists[1]) then
return 0;
fi;
ret := AVL.RemoveSetInner(avl!.lists[1], val, avl!.isLess, AVL.Remove_Extremal, AVL.Trinode, AVL.RemoveThisNode);
if ret = fail then
return 0;
fi;
if ret[2] <> fail then
avl!.lists[1] := ret[2];
else
Unbind(avl!.lists[1]);
fi;
return 1;
end);
#
# Utility to compute actual imbalances of every node and Assert that the
# stored data is correct
#
AVL.AVLCheck := function(avl)
local avlh, l;
avlh := function(node)
local p, resl, resr;
Assert(1, IsBound(node[2]));
p := Position(l, node[2]);
Assert(1, p <> fail);
Assert(1, IsBound(node[4]));
if AVL.hasLeft(node[4]) then
Assert(1, IsBound(node[1]));
resl := avlh(node[1]);
else
resl := [0,0];
if p = 1 then
Assert(1,not IsBound(node[1]));
else
Assert(1, IsBound(node[1]));
Assert(1,node[1][2] = l[p-1]);
fi;
fi;
if AVL.hasRight(node[4]) then
Assert(1, IsBound(node[3]));
resr := avlh(node[3]);
else
resr := [0,0];
if p = Length(l) then
Assert(1,not IsBound(node[3]));
else
Assert(1, IsBound(node[3]));
Assert(1,node[3][2] = l[p+1]);
fi;
fi;
Assert(1,AVL.getImbalance(node[4]) = resr[1]-resl[1] + 1);
Assert(1,AVL.getSubtreeSize(node[4]) = resr[2] + resl[2] + 1);
return [1 + Maximum(resr[1], resl[1]), 1 + resl[2] + resr[2]];
end;
if not IsEmpty(avl) then
l := AsList(avl);
avlh(avl!.lists[1]);
fi;
end;
[ Dauer der Verarbeitung: 0.42 Sekunden
(vorverarbeitet)
]
|