# # 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. #
AVL.DefaultType := NewType(OrderedSetDSFamily, IsAVLTreeRep and IsOrderedSetDS and IsMutable);
AVL.DefaultTypeStandard := NewType(OrderedSetDSFamily, IsAVLTreeRep and IsStandardOrderedSetDS and IsMutable);
AVL.nullIterator := Iterator([]);
# # 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;
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 #
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,
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 = 0then
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 = 1then # # This can only happen while deleting #
aflags := sim(aflags,dirn);
yflags := sim(yflags,2-dirn);
htchange := 0;
# # 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 = 1then
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;
# # 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;
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 = 0then
avl[4] := ss(avl[4],gs(avl[4])+1);
return 0; elif deeper = fail then
return fail; elif deeper = 1then # # 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) > 0do
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+1then
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 = 0then
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 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 = 0then
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 = 0then
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;
# # if the subtree got shorter then adjust balance #
if res[1] = -1then
im := AVL.getImbalance(flags); if im = dirn then
l[4] := AVL.setImbalance(flags,1);
return [-1, res[2], l, newext]; elif im = 1then
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 = 2then
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;
# Adjust balance # if res[1] <> 0then if im <> 1then # # 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;
# # 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] = 0then # # 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-1then
node[4] := AVL.setImbalance(flags, 1);
ret[2] := node;
return ret; elif im = 1then
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 = 1then
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;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.