public void Insert(int data) { RBTreeNode newNode; RBTreeNode y = null; RBTreeNode x; if (Root == null) // Empty tree { Root = new RBTreeNode(data, red: false); return; } newNode = new RBTreeNode(data); // Instantiate as Red node x = Root; // Traverse until y is a leaf in a location where x doesn’t violate // the tree’s ordering while (x != null) { y = x; if (newNode < x) { x = x.Left(); } else { x = x.Right(); } } // Make newNode child of y newNode.SetParent(y); if (newNode < y) { y.SetLeft(newNode); } else { y.SetRight(newNode); } RebalancePostInsertion(newNode); }
private void RotateLeft(RBTreeNode x) { /** * Make right subtree left subtree */ // setup x & y RBTreeNode y = x.Right(); x.SetRight(y.Left()); // if y has a left child, make x the parent of the left child of y. if (y.Left() != null) { y.Left().SetParent(x); } y.SetParent(x.Parent()); // if x has no parent, make y the root of the tree. if (x.Parent() == null) { Root = y; } // else if x is the left child of p, make y the left child of p. else if (x == x.Parent().Left()) { x.Parent().SetLeft(y); } // else make y the right child of p else { x.Parent().SetRight(y); } // make y the parent of x y.SetLeft(x); x.SetParent(y); }
private void RotateRight(RBTreeNode y) { /** * Make left subtree right subtree */ // setup x & y RBTreeNode x = y.Left(); y.SetLeft(x.Right()); // if x has a right child, make y the parent of the right child of x. if (x.Right() != null) { x.Right().SetParent(y); } x.SetParent(y.Parent()); // if y has no parent, make x the root of the tree. if (y.Parent() == null) { Root = x; } // else if y is the right child of its parent p, make x the right child of p. else if (y == y.Parent().Right()) { y.Parent().SetRight(x); } // else assign x as the left child of p. else { y.Parent().SetLeft(x); } // make x the parent of y. x.SetRight(y); y.SetParent(x); }
private void Transplant(RBTreeNode oldNode, RBTreeNode newNode) { // Replace oldNode with newNode RBTreeNode parent = oldNode.Parent(); if (parent == null) { Root = newNode; } else if (oldNode == parent.Left()) { parent.SetLeft(newNode); } else { parent.SetRight(newNode); } // Null check for when newNode is null if (newNode != null) { newNode.SetParent(parent); } }
public void SetRight(RBTreeNode rightChild) { RightChild = rightChild; }
public void SetLeft(RBTreeNode leftChild) { LeftChild = leftChild; }
public void SetParent(RBTreeNode parent) { ParentNode = parent; }
public string VisualizeTree(RBTreeNode node, int verticalSpacing, int indentPerLevel, bool print = true, bool safeChars = true) { /** * Parameters: * node: RBTree node treated as the root in the visualization * verticalSpacing: number of lines between each displayed node * indentPerLevel: width of the line separating each level of the tree * print: if true, write output to console * safeChars: if true, use standard dash '-' character for horizontal lines. * else, use the special full-width horizontal bar character * which seems to occasionally break StringBuilder * * Note: functionality inspired by the following Baeldung article: * https://www.baeldung.com/java-print-binary-tree-diagram */ string pointerChars; if (safeChars) { pointerChars = new string('-', indentPerLevel); } else { pointerChars = new string('─', indentPerLevel); } string oneChildPointer = "└" + pointerChars; string twoChildrenPointer = "├" + pointerChars; string paddingChars = new string(' ', indentPerLevel + 1); string twoChildrenIndent = "│" + paddingChars; string whiteSpaceIndent = " " + paddingChars; void Traverse(StringBuilder sb, String padding, String pointer, RBTreeNode node, bool hasRightSibling) { if (node == null || node.IsEmpty()) { return; } // Append vertical line spacing for (int i = 0; i < verticalSpacing; i++) { sb.Append("\n" + padding); if (pointer == whiteSpaceIndent) { sb.Append('|'); } else { sb.Append("| "); } } // Append line rows with values sb.Append($"\n{padding}{pointer} {node.VisualizerString()}"); // Calculate and append padding next row StringBuilder paddingSB = new StringBuilder(padding); if (hasRightSibling) { paddingSB.Append(twoChildrenIndent); } else { paddingSB.Append(whiteSpaceIndent); } string newPadding = paddingSB.ToString(); // Determine pointer for next row string pointerLeft = (node.Right() != null) ? twoChildrenPointer : oneChildPointer; // Recurse Traverse(sb, newPadding, pointerLeft, node.Left(), node.Right() != null); Traverse(sb, newPadding, oneChildPointer, node.Right(), false); } string TraversePreOrder(RBTreeNode root) { // Handle root if (root == null) { return("Empty binary tree."); } StringBuilder sb = new StringBuilder(); sb.Append(root.GetData() + " (B)"); // Determine initial pointer string pointerLeft = (root.Right() != null) ? twoChildrenPointer : oneChildPointer; Traverse(sb, "", pointerLeft, root.Left(), root.Right() != null); Traverse(sb, "", oneChildPointer, root.Right(), false); return(sb.ToString()); } string prettyBinaryTree = TraversePreOrder(node); if (print) { Console.WriteLine(prettyBinaryTree); } return(prettyBinaryTree); }
public string PrintTreeTraversalOrder(int order, RBTreeNode treeRoot) { /** * Output to console & return as string. * * Parameters: * order: 1 -> Inorder * 2 -> Preorder * 3 -> Postorder * treeRoot: the root node of the Red-Black tree to traverse */ // Helpers: void PrintInOrder(RBTreeNode currRoot) { if (currRoot == null) { return; } PrintInOrder(currRoot.Left()); Console.Write($"{currRoot.GetData()}, "); PrintInOrder(currRoot.Right()); } void PrintPreOrder(RBTreeNode currRoot) { if (currRoot == null) { return; } Console.Write($"{currRoot.GetData()}, "); PrintPreOrder(currRoot.Left()); PrintPreOrder(currRoot.Right()); } void PrintPostOrder(RBTreeNode currRoot) { if (currRoot == null) { return; } PrintPostOrder(currRoot.Left()); PrintPostOrder(currRoot.Right()); Console.Write($"{currRoot.GetData()}, "); } // Write to string using console: StringWriter sw = new StringWriter(); Console.SetOut(sw); if (order == 1) { Console.Write("Inorder traversal: "); PrintInOrder(treeRoot); } else if (order == 2) { Console.Write("Preorder traversal: "); PrintPreOrder(treeRoot); } else if (order == 3) { Console.Write("Postorder traversal: "); PrintPostOrder(treeRoot); } else { throw new Exception( $"Invalid order parameter {order} when calling traverse."); } // store string & remove trailing comma & space String output = sw.ToString(); output = output.Remove(output.Length - 2, 2); // close StringWriter, reset standard out, print, and return sw.Close(); var standardOutput = new StreamWriter(Console.OpenStandardOutput()); standardOutput.AutoFlush = true; Console.SetOut(standardOutput); Console.WriteLine(output); return(output); }
private void RebalancePostInsertion(RBTreeNode newNode) { /** * Insert function places newNode as an appropriate leaf for a regular BST but * not necessarily a Red-black tree. Check if newNode's parent is RED (ie. if it * breaks a only red-black tree condition). If so, use the RED and BLACK labels * of newNode's parent, grandparent, and/or pibling (aunt/uncle) to balance. * * Note on conceptualizing this process: * At this point, newNode is a RED leaf whose addition may cause the tree to * not meet the required RED-BLACK conditions (noted in class description) * such that it is imbalanced beyond the red-black tree threshold. In order * to correct for this, we re-balance and/or recolor the subtree rooted at * newNode's grandparent. We perform this process iteratively, reassigning * newNode to its decendants (parents/grandparents), until we reach the root. * At no point do we consider ancestors of newNode. * * See this page for a more detailed overview of the process: * https://www.geeksforgeeks.org/red-black-tree-set-2-insert/ */ if (newNode.GrandParent() == null) { return; } RBTreeNode u; // Will represent pibling (aunt/uncle) or parent of newNode. // ie. given newNode's grandparent (gP), u can be either gP's // left or right child while (newNode.Parent().IsRed()) { // Case: p is the RIGHT child of gP: if (newNode.Parent() == newNode.GrandParent().Right()) { // Set u as the (LEFT) pibling of newNode u = newNode.GrandParent().Left(); // Case: u exists and is RED: set both u & p to BLACK, set gP to RED, // assign newNode = gP if (u != null && u.IsRed()) { u.SetBlack(); newNode.Parent().SetBlack(); newNode.GrandParent().SetRed(); newNode = newNode.GrandParent(); } else // Case: the (LEFT) pibling of newNode is BLACK // Case: newNode is the left child of p: assign newNode = p and // right rotate newNode { if (newNode == newNode.Parent().Left()) { newNode = newNode.Parent(); RotateRight(newNode); } // Set p to BLACK and gP to RED. Then left rotate gP. newNode.Parent().SetBlack(); newNode.GrandParent().SetRed(); RotateLeft(newNode.GrandParent()); } } else // Case: p is the LEFT child of gP: // Assign u as the (RIGHT) pibling of newNode { u = newNode.GrandParent().Right(); // Case: u is RED: set the color of both children of gP to BLACK, set // gp to RED. Assign gP = newNode. if (u != null && u.IsRed()) { u.SetBlack(); newNode.Parent().SetBlack(); newNode.GrandParent().SetRed(); newNode = newNode.GrandParent(); } else { // Case: newNode is the right child of p then: set p = newNode. // Then left rotate newNode. if (newNode == newNode.Parent().Right()) { newNode = newNode.Parent(); RotateLeft(newNode); } // Set color of p as BLACK and color of gP as RED. Then right // rotate gP. newNode.Parent().SetBlack(); newNode.GrandParent().SetRed(); RotateRight(newNode.GrandParent()); } } if (newNode == Root) { break; } } Root.SetBlack(); }
public void Delete(RBTreeNode node, int value) { // Traverse until we find nodeToDelete RBTreeNode nodeToDelete = null; while (node != null) { if (node.GetData() == value) { nodeToDelete = node; break; } node = (value < node.GetData()) ? node.Left() : node.Right(); } if (nodeToDelete == null) { throw new Exception("Cannot find node to delete with " + $"value {value} in Red Black Tree"); } RBTreeNode x, y; // Save the color of nodeToDelete bool originallyRed = nodeToDelete.IsRed(); // If nodeToDelete only has left children, replace with its left subtree if (nodeToDelete.Left() == null || nodeToDelete.Left().IsEmpty()) { x = nodeToDelete.Right(); if (x == null) { x = Leaf; } Transplant(nodeToDelete, x); // If nodeToDelete only has right children, replace with its right subtree } else if (nodeToDelete.Right() == null || nodeToDelete.Right().IsEmpty()) { x = nodeToDelete.Left(); if (x == null) { x = Leaf; } Transplant(nodeToDelete, x); } else // nodeToDelete either has two children or is a leaf // y = smallest node whose value is greater than that of nodeToDelete // will replace nodeToDelete { y = Minimum(nodeToDelete.Right()); originallyRed = y.IsRed(); // x = subtree root with values greater than y x = y.Right(); if (x == null) { x = Leaf; } // if y is a child of nodeToDelete, x is already stored in the right place if (y.Parent() == nodeToDelete) { x.SetParent(y); } else // else replace y with its right subtree and update attributes { Transplant(y, y.Right()); y.SetRight(nodeToDelete.Right()); y.Right().SetParent(y); } // replace nodeToDelete with y and update attributes Transplant(nodeToDelete, y); y.SetLeft(nodeToDelete.Left()); y.Left().SetParent(y); // y will replace nodeToDelete's location in tree, update its color if (originallyRed) { y.SetRed(); } else { y.SetBlack(); } } if (!(originallyRed)) { RebalancePostDeletion(x); } }
private void RebalancePostDeletion(RBTreeNode node) { // Very similar to RebalancePostInsertion function // node is the node in the location of/closest to the node just deleted RBTreeNode s; // represents sibbling of node while (node != Root && node.IsBlack()) { if (node == node.Parent().Left()) { s = node.Parent().Right(); if (s.IsRed()) // case 1 { s.SetBlack(); node.Parent().SetRed(); RotateLeft(node.Parent()); s = node.Parent().Right(); } if (s.Left().IsBlack() && s.Right().IsBlack()) // case 2 { s.SetRed(); node = node.Parent(); } else { if (s.Right().IsBlack()) // case 3 { s.Left().SetBlack(); s.SetRed(); RotateRight(s); s = node.Parent().Right(); } if (s.Parent().IsRed()) { s.SetRed(); } else { s.SetBlack(); } node.Parent().SetBlack(); s.Right().SetBlack(); RotateLeft(s.Parent()); node = Root; } } else { s = node.Parent().Left(); if (s.IsRed()) { s.SetBlack(); node.Parent().SetRed(); RotateRight(node.Parent()); s = node.Parent().Left(); } if (s.Right() == null) { s.SetRight(Leaf); } if (s.Left() == null) { s.SetLeft(Leaf); } if (s.Right().IsBlack() && s.Left().IsBlack()) { s.SetRed(); node = node.Parent(); } else { if (s.Left().IsBlack()) { s.Right().SetBlack(); s.SetRed(); RotateLeft(s); s = node.Parent().Left(); } if (s.Parent().IsRed()) { s.SetRed(); } else { s.SetBlack(); } node.Parent().SetBlack(); s.Left().SetBlack(); RotateRight(node.Parent()); node = Root; } } } node.SetBlack(); }