public static T Build <T>(IEnumerable <T> items, IAccessors <T> ops) where T : notnull { var iter = items.GetEnumerator(); if (!iter.MoveNext()) { throw new Exception("Cannot build a Red/Black tree from an empty collection"); } // Use the first item as the root var root = iter.Current; ops.Colour(root, EColour.Black); // Insert all items into the tree for (; iter.MoveNext();) { root = Insert(root, iter.Current, ops); Check(root, ops); } return(root); }
public static T Insert <T>([DisallowNull] T root, [DisallowNull] T item, IAccessors <T> ops) where T : notnull { using var stack_inst = StackPool <T> .Instance.Alloc(); var stack = stack_inst.Value; // Insert 'item'. Record the path down the tree in 'stack' stack.Push(root); for (; ;) { var side = ops.Compare(item, stack.Top !) < 0 ? Left : Right; if (ops.Child(side, stack.Top) is T child) { stack.Push(child); } else { ops.Child(side, stack.Top, item); stack.Push(item); break; } } // Climb back to the root node, fixing colours as we go for (; stack.Count > 2;) { // Get the leaf and its parent var node = stack.Pop(); var parent = stack.Pop(); // If either 'node' or 'parent' is black, no more changes are needed if (ops.Colour(node) == EColour.Black || ops.Colour(parent) == EColour.Black) { break; } var gparent = stack.Pop(); var parent_side = Side(parent, gparent, ops); // Red Uncle if (ops.Child(-parent_side, gparent) is T uncle && ops.Colour(uncle) == EColour.Red) { // Recolour ops.Colour(gparent, EColour.Red); ops.Colour(parent, EColour.Black); ops.Colour(uncle, EColour.Black); stack.Push(gparent); continue; } // Black Uncle // If 'node_side' is not equal to 'parent_side', swap 'node' and 'parent' to transform case 2 -> case 3 var node_side = Side(node, parent, ops); if (node_side != parent_side) { Rotate(-node_side, parent, gparent, ops); } // Case 3 -> swap 'parent' and 'gparent' and recolour ops.Colour(gparent, EColour.Red); gparent = Rotate <T>(-parent_side, gparent, stack.Top, ops); ops.Colour(gparent, EColour.Black); // If we rotate the root element, update 'root' if (stack.Count == 0) { root = gparent; } } // Return the root, ensuring it's black ops.Child(0, default, root);