Implementing an AVL tree using a List - c#

So I have implemented an Binary search tree using a parameterized List i.e.
List<Node> tree = new List<>();
The tree works fine. The node itself doesn't know anything about its parent or children. This is because I calculate the locations based on the index e.g.
If i is the index of some None N then:
N's left child is in tree[i*2]
N's right child is in tree[(i*2)+1]
This binary tree works fine. But now I want to put AVL tree features to it. Im am stuck at this point because I do not know how to make the rotations on a List. On rotation, how do i move the children of the new root? Fact is they have to shift indexes don't they? Also doing this on an List gives me the problem that displaying the tree will require looping through the List everytime i add a node. THis wont happen in O(logn) anymore destroying the whole point of an AVL tree.
I am doing this in C#. I just want to know how to make this AVL tree efficiently using a List or any array based data structure thats indexable and not a Linked list. This is important.Some code to illustrate would be greatly appreciated.

Representing a tree in an array/list the way you are doing is common for the heap data structure, but it does not work for virtually any other kind of tree. In particular, you cannot do this (efficiently) for AVL trees because each rotation would require too much copying.

I had a need for this for an embedded application where we did not have malloc available. Having not any done any kind of data structure algorithm implementation before I was trying if it could be done. While writing code I realized I would have to move a lot of things around. I searched for a remedy and got to this post.
Thanks to Chris's reply, I am not going to spend any more time on it. I will find some other way to implement what I need.

I believe I found an answer, the Trick is to move subtrees up and down the list so that you don't overwrite valid nodes while rotating.
void shiftUp(int indx, int towards) {
if (indx >= size || nodes[indx].key == NULL) {
return;
}
nodes[towards] = nodes[indx];
nodes[indx].key = NULL;
shiftUp(lChild(indx), lChild(towards));
shiftUp(rChild(indx), rChild(towards));
}
void shiftDown(int indx, int towards) {
if (indx >= size || nodes[indx].key == NULL) {
return;
}
// increase size so we can finish shifting down
while (towards >= size) { // while in the case we don't make it big enough
enlarge();
}
shiftDown(lChild(indx), lChild(towards));
shiftDown(rChild(indx), rChild(towards));
nodes[towards] = nodes[indx];
nodes[indx].key = NULL;
}
As you can see this is done by exploring recursively each subtree until the NULL (defined in this as -1) nodes then copying each element one by one up or down.
with this we can define the 4 types of rotations named according this Wikipedia Tree_Rebalancing.gif
void rotateRight(int rootIndx) {
int pivotIndx = lChild(rootIndx);
// shift the roots right subtree down to the right
shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
nodes[rChild(rootIndx)] = nodes[rootIndx]; // move root too
// move the pivots right child to the roots right child's left child
shiftDown(rChild(pivotIndx), lChild(rChild(rootIndx)));
// move the pivot up to the root
shiftUp(pivotIndx, rootIndx);
// adjust balances of nodes in their new positions
nodes[rootIndx].balance--; // old pivot
nodes[rChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}
void rotateLeft(int rootIndx) {
int pivotIndx = rChild(rootIndx);
// Shift the roots left subtree down to the left
shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
nodes[lChild(rootIndx)] = nodes[rootIndx]; // move root too
// move the pivots left child to the roots left child's right child
shiftDown(lChild(pivotIndx), rChild(lChild(rootIndx)));
// move the pivot up to the root
shiftUp(pivotIndx, rootIndx);
// adjust balances of nodes in their new positions
nodes[rootIndx].balance++; // old pivot
nodes[lChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}
// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateLeftRight(int rootIndx) {
int newRootIndx = rChild(lChild(rootIndx));
// shift the root's right subtree down to the right
shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
nodes[rChild(rootIndx)] = nodes[rootIndx];
// move the new roots right child to the roots right child's left child
shiftUp(rChild(newRootIndx), lChild(rChild(rootIndx)));
// move the new root node into the root node
nodes[rootIndx] = nodes[newRootIndx];
nodes[newRootIndx].key = NULL;
// shift up to where the new root was, it's left child
shiftUp(lChild(newRootIndx), newRootIndx);
// adjust balances of nodes in their new positions
if (nodes[rootIndx].balance == -1) { // new root
nodes[rChild(rootIndx)].balance = 0; // old root
nodes[lChild(rootIndx)].balance = 1; // left from old root
} else if (nodes[rootIndx].balance == 0) {
nodes[rChild(rootIndx)].balance = 0;
nodes[lChild(rootIndx)].balance = 0;
} else {
nodes[rChild(rootIndx)].balance = -1;
nodes[lChild(rootIndx)].balance = 0;
}
nodes[rootIndx].balance = 0;
}
// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateRightLeft(int rootIndx) {
int newRootIndx = lChild(rChild(rootIndx));
// shift the root's left subtree down to the left
shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
nodes[lChild(rootIndx)] = nodes[rootIndx];
// move the new roots left child to the roots left child's right child
shiftUp(lChild(newRootIndx), rChild(lChild(rootIndx)));
// move the new root node into the root node
nodes[rootIndx] = nodes[newRootIndx];
nodes[newRootIndx].key = NULL;
// shift up to where the new root was it's right child
shiftUp(rChild(newRootIndx), newRootIndx);
// adjust balances of nodes in their new positions
if (nodes[rootIndx].balance == 1) { // new root
nodes[lChild(rootIndx)].balance = 0; // old root
nodes[rChild(rootIndx)].balance = -1; // right from old root
} else if (nodes[rootIndx].balance == 0) {
nodes[lChild(rootIndx)].balance = 0;
nodes[rChild(rootIndx)].balance = 0;
} else {
nodes[lChild(rootIndx)].balance = 1;
nodes[rChild(rootIndx)].balance = 0;
}
nodes[rootIndx].balance = 0;
}
Note that in cases where shifting would overwrite nodes we just copy the single node
As for efficiency storing the balance in each node would be a must as getting the differences of heights at each node would be quite costly
int getHeight(int indx) {
if (indx >= size || nodes[indx].key == NULL) {
return 0;
} else {
return max(getHeight(lChild(indx)) + 1, getHeight(rChild(indx)) + 1);
}
}
Though doing this requires us to update the balance at affected nodes when modifying the list, though this can be somewhat efficiently by only updating strictly necessary cases.
for deletion this adjustment is
// requires non null node index and a balance factor baised off whitch child of it's parent it is or 0
private void deleteNode(int i, short balance) {
int lChildIndx = lChild(i);
int rChildIndx = rChild(i);
count--;
if (nodes[lChildIndx].key == NULL) {
if (nodes[rChildIndx].key == NULL) {
// root or leaf
nodes[i].key = NULL;
if (i != 0) {
deleteBalance(parent(i), balance);
}
} else {
shiftUp(rChildIndx, i);
deleteBalance(i, 0);
}
} else if (nodes[rChildIndx].key == NULL) {
shiftUp(lChildIndx, i);
deleteBalance(i, 0);
} else {
int successorIndx = rChildIndx;
// replace node with smallest child in the right subtree
if (nodes[lChild(successorIndx)].key == NULL) {
nodes[successorIndx].balance = nodes[i].balance;
shiftUp(successorIndx, i);
deleteBalance(successorIndx, 1);
} else {
int tempLeft;
while ((tempLeft = lChild(successorIndx)) != NULL) {
successorIndx = tempLeft;
}
nodes[successorIndx].balance = nodes[i].balance;
nodes[i] = nodes[successorIndx];
shiftUp(rChild(successorIndx), successorIndx);
deleteBalance(parent(successorIndx), -1);
}
}
}
similarly for insertion this is
void insertBalance(int pviotIndx, short balance) {
while (pviotIndx != NULL) {
balance = (nodes[pviotIndx].balance += balance);
if (balance == 0) {
return;
} else if (balance == 2) {
if (nodes[lChild(pviotIndx)].balance == 1) {
rotateRight(pviotIndx);
} else {
rotateLeftRight(pviotIndx);
}
return;
} else if (balance == -2) {
if (nodes[rChild(pviotIndx)].balance == -1) {
rotateLeft(pviotIndx);
} else {
rotateRightLeft(pviotIndx);
}
return;
}
int p = parent(pviotIndx);
if (p != NULL) {
balance = lChild(p) == pviotIndx ? (short)1 : (short)-1;
}
pviotIndx = p;
}
}
As you can see this just uses plain arrays of "node"s as i translated it from c code given gitHub array-avl-tree and optimizations and balancing from (a link i'll post in a comment) but would work quite similar in a List
Finally I have minimal knowledge of AVL trees, or optimal implementations so i don't claim that this is bug free or the most efficient but have passed my preliminary tests at least for my purposes

Related

Advice on an A-star script in Unity3D

I'm implementing A* in Unity so my NPCs can navigate through the world.
The nodes are Vector3s in the game, and they have a class called WaypointController on them.
Each waypoint has a list of adjacent nodes in the graph.
All my A* function needs to do is start at the closest node to the NPC that is within their line of sight, then do A* to the node within line of sight of where they're headed and closest.
I am pretty sure my A* finds the node with the lowest f score in the open set.
Then takes it out of open and puts it into the closed set.
Then breaks if current node is the destination.
Then looks at all nodes adjacent to the current node, and updates their f score and h score, and sets current node as the adjacent node's node before it.
Something is definitely wrong because it crashes unity when I call it.
...
public List GetPath(WaypointController start, WaypointController end)
{
List path = new List();
//Don't interrupt other GetPath calls
if (findingPath == false)
{
findingPath = true;
//nodes to evaluate
List<WaypointController> openNodes = new List<WaypointController>();
//evaluated nodes
List<WaypointController> closedNodes = new List<WaypointController>();
//Setup start & end
start.pathLengthToA = 0;
start.distanceToZ = Vector3.Distance(start.transform.position, end.transform.position);
end.distanceToZ = 0;
//node being evaluated
WaypointController currentNode = start;
//Add start node to open
openNodes.Add(currentNode);
//Evaluate nodes in the open set until currentNode = end
do {
//currentNode = lowest F cost node in openNodes
foreach(WaypointController openWPC in openNodes)
{
if(openWPC.pathLengthToA + openWPC.distanceToZ < currentNode.pathLengthToA + currentNode.distanceToZ)
{
Debug.Log("A* using: " + openWPC);
currentNode = openWPC;
}
}
//Move currentNode from openNodes to closedNodes
openNodes.Remove(currentNode);
closedNodes.Add(currentNode);
//Break if path complete
if(currentNode == end)
break;
//For each adjacent node to currentNode
float tempEdgeLength;
foreach(WaypointController adjWPC in currentNode.adjacentNodes)
{
//Skip closed nodes
if(closedNodes.Contains(adjWPC))
continue;
//If new path to adjacent node is shorter, or adjacent node is not in open (set it up)
graph.TryGetEdge(currentNode, adjWPC, out tempEdgeLength);
if(currentNode.pathLengthToA + tempEdgeLength < adjWPC.pathLengthToA || openNodes.Contains(adjWPC) == false)
{
Debug.Log("A* is evaluating node: " + adjWPC.name);
//set f cost of neighbor
adjWPC.pathLengthToA = currentNode.pathLengthToA + tempEdgeLength;
adjWPC.distanceToZ = Vector3.Distance(adjWPC.transform.position, end.transform.position);
//set nodeBeforeThisInPath in neighbor to currentNode
adjWPC.nodeBeforeThisInPath = currentNode;
//add neighbor to openNodes
if(openNodes.Contains(adjWPC) == false)
openNodes.Add(adjWPC);
}
}
} while (currentNode != end);
//Output it
path.Add(start.transform.position);
while (currentNode.nodeBeforeThisInPath != start)
{
path.Add(currentNode.nodeBeforeThisInPath.transform.position);
}
path.Add(end.transform.position);
//free up the function
findingPath = false;
}
else
{
path.Add(Vector3.zero);
}
//Return the path or an empty path if findingPath was already true (& handle retry in the other script
return path;
}
...
Thanks for joining me on this puzzle.
I will post any updates as I discover them.

How to prevent a child destroying in Unity

I am preparing a puzzle game. I can drag and drop from down generated sticks to up places. Let's say that there are two sticks on the first node and we have 2 more sticks in bottom. We can drag it to right place and make them one parent again. I can make them one parent but second stick from buttom is destroying when i drop on the first node.
In this picture it is aqua color:
before drop
after drop
How to resolve this issue?
if(nodes[smallestId].transform.childCount > 0)
{
for (int i = 0; i < rayhit.transform.childCount; i++)
{
nodes[smallestId].transform.GetChild(0).GetComponent<Node>().sticks.Add(rayhit.transform.GetChild(i).GetComponent<Stick>());
rayhit.transform.GetChild(i).transform.SetParent(nodes[smallestId].transform.GetChild(0));
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(nodes[smallestId].transform);
Debug.Log("Stick : " + nodes[smallestId].transform);
}
The issue is that you iterate over rayhit.transform.childCount.
When you remove a child due to rayhit.transform.GetChild(i).transform.SetParent the rayhit.transform.childCount is already reduced by one.
Therefore in the next iteration i might already be greater then rayhit.transform.childCount so e.g. the second object is not moved to the new parent!
Instead You could use
// do this only once
var targetParent = nodes[smallestId].transform;
if(targetParent > 0)
{
targetParent = targetParent.GetChild(0);
// do also this only once
var node = targetParent.GetComponent<Node>();
var toMove = new List<Transform>();
foreach (Transform child in rayhit.transform)
{
node.sticks.Add(child.GetComponent<Stick>());
toMove.Add(child);
}
foreach(var child in toMove)
{
child.SetParent(targetParent);
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(targetParent);
Debug.Log("Stick : " + targetParent);
}
Which first stores them all in a list so you don't depend on whether they already where moved to a new parent or not.
As alternative I think you could even rather use GetComponentsInChildren here which would completely save you from the trouble and you wouldn't need to use two loops at all:
var targetParent = nodes[smallestId].transform;
if(targetParent > 0)
{
targetParent = targetParent.GetChild(0);
// do also this only once
var node = targetParent.GetComponent<Node>();
// These references now don't depend on their parent object
// so you can safely iterate on them without any issues
foreach (var stick in rayhit.transform.GetComponentsInChildren<Stick>(true))
{
node.sticks.Add(stick);
stick.transform.SetParent(targetParent);
}
Destroy(rayhit.transform.gameObject);
}
else
{
rayhit.transform.SetParent(targetParent);
Debug.Log("Stick : " + targetParent);
}
guys here is solution :)
rayhit.transform.position = nodes[smallestId].transform.position;
if (rayhit.transform.parent != nodes[smallestId].transform)
{
if (nodes[smallestId].transform.childCount > 0 && nodes[smallestId].transform != rayhit.transform.parent)
{
if (currNode != null)
{
for (int i = 0; i < currNode.sticks.Count; i++)
{
nodes[smallestId].transform.GetChild(0).GetComponent<Node>().sticks.Add(currNode.sticks[i]);
currNode.sticks[i].transform.SetParent(nodes[smallestId].transform.GetChild(0));
}
Destroy(rayhit.transform.gameObject);
}
}
else
{
if (currNode != null)
{
currNode.isMoved = true;
}
rayhit.transform.SetParent(nodes[smallestId].transform);
}
}
Thank you for your help.

Remove an element on a given position from a LinkedList

I try to remove an element after a specified node, code doesn't give me an error but doesn't to its job .
public List<int> list;
public class Node
{
public object Cont;
public Node Next;
}
public Node head;
linkedlist()
{
head = null;
}
public linkedlist(object value)
{
head = new Node();
head.Cont = value;
}
public void removeAfter(int nume1)
{
Node currentnode = head;
int current = 0;
while (current <= nume1 && currentnode.Next != null)
{
currentnode = currentnode.Next;
current++;
}
currentnode = currentnode.Next;
}
I try to remove the element on the "nume1"th position from the LinkedList.
I know c# has a built-in LinkedList but I need to work on this one. The file has more code but I think this is enough to get a proper answer
In order to remove an item after another item in a singly-linked list, we simply need to set Item.Next = Item.Next.Next. This effectively removes the Node at Item.Next from the list because:
If we have a node "first" at index 0, then it's Next will point to "second" at index 1, and second.Next will point to "third" at index 2
So first.Next.Next points to "third" at index 2
And setting first.Next = first.Next.Next changes the next item for "first" to "third".
For example:
class Node
{
public string Data { get; set; }
public Node Next { get; set; }
}
class LinkedList
{
public Node Head { get; private set; }
public void Add(string data)
{
var node = new Node {Data = data};
if (Head == null)
{
Head = node;
}
else
{
var current = Head;
while (current.Next != null) current = current.Next;
current.Next = node;
}
}
public void RemoveAfterIndex(int index)
{
if (index < -1) return;
if (index == -1)
{
Head = Head.Next;
return;
}
var current = Head;
var count = 0;
while (count < index && current.Next != null)
{
current = current.Next;
count++;
}
current.Next = current.Next?.Next;
}
public void WriteNodes()
{
var current = Head;
var index = 0;
while (current != null)
{
Console.WriteLine(current.Data.PadRight(10) + $" (index {index++})");
current = current.Next;
}
}
}
If we wanted to create a list with 10 nodes, and then remove the node after the node at index 3, it might look something like:
private static void Main()
{
var linkedList = new LinkedList();
for (int i = 0; i < 10; i++)
{
linkedList.Add($"Item #{i + 1}");
}
linkedList.WriteNodes();
var dash = new string('-', 25);
Console.WriteLine($"{dash}\nCalling: RemoveAfterIndex(3)\n{dash}");
linkedList.RemoveAfterIndex(3);
linkedList.WriteNodes();
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
In a single-linked list you remove element n by setting Next on element n-1 to element n+1. Since each element has a reference to the following element you need to iterate through the list until you locate element n, retaining the previous element as you go. Then it's just a simple assignment to cut the element from the list.
In code this looks something like:
public void RemoveAt(int position)
{
// reference to current position in list
Node current = head;
// check for empty list
if (current == null)
return;
// special case if we're removing the first item in the list
if (position == 0)
{
root = current.Next;
return;
}
// reference to previous position in list
Node previous = null;
// scan through the array to locate the item to remove
for (int i = 0; i < position && current != null; i++)
{
// update previous reference
previous = current;
// step to next element in list
current = current.Next;
}
if (previous != null && current != null)
{
previous.Next = current.Next;
}
}
Where you say this:
currentnode = currentnode.Next;
You should consider saying this:
currentnode.Next = currentnode.Next?.Next;
If you think about it, to remove the 5th node in a list, you want to make the .Next property of the 4th node, point to node 6. If you're looking at the 4th node, you want its .Next (pointing to 5) to be the .Next.Next (pointing to 5.pointing to 6) instead.
The ? after the first .Next helps us out if we are at the end of the list. If we are looking at the last item in a list, then .Next is null, so calling .Next.Next would result in a NullReferenceException. The ?prevents this by realising the first .Next is null and not trying to evaluate the second .Next on something that is null, but instead stopping evaluation early and returning a null at that point
It is conceptually equivalent to
currentnode.Next = (currentnode.Next == null ? null : currentNode.Next.Next);
It can be done any number of times:
a = b.C?.D?.E?.F;
If any of C, D, E is null, a will end up null without causing an exception. You don't need it after F because if only the last thing is null no exception occurs - we aren't accessing any method or property of the null F
Couple of other points:
Microsoft's own LinkedList has a method of removing nodes based on position, called RemoveAt. It's more useful than RemoveAfter because if can remove the first node in the list, whereas RemoveAfter cannot, unless you take the slightly counterintuitive approach that supplying any index less than the start index will remove the first node (e.g RemoveAfter(-1)). You could create a RemoveBefore or RemoveFirst, but it does add a bit of unnecessary complexity. Generally if taking the academic exercise of re-implementing a class that already exists somewhere in the framework, it's a sensible notion to model your methods on the existing one then it's a good overall fit for "the Microsoft way" in which the rest of the framework is written; fellow developers used to "the Microsoft way" will appreciate it if you're writing a library for them to use
Your naming convention (lowercase first letter for method) is java form, and I did wonder if you were using java but tagged c# for some reason. I'm not sure if null promoting ? exists in java- if it doesn't the inline-if form given last will also work fine.

Drawing a Symmetric Binary B-Tree

I have a little "project" which involves drawing Symmetric Binary B-Trees, like this one:
But I cant figure out a way to correctly calculate the position (x,y) of each node. The way Im doing it right now, as the tree's height grows some nodes tend to get overlapped by others.
Can anyone give me some light as to how can I calculate the position of a node?
Im using C# and this is the class I have right now that represents a node:
class SBBTreeNode<T> where T : IComparable {
public SBBTreeNode(T item) {
Data = item;
Left = null;
Right = null;
}
public T Data { get; private set; }
public SBBTreeNode<T> Left;
public SBBTreeNode<T> Right;
public bool IsHorizontal { get; set; } //Is this node horizontal?
public bool IsLeaf() {
return Left == null && Right == null;
}
}
Here is a drawing routine:
void drawTree(Graphics G)
{
if (flatTree.Count <= 0) return;
if (maxItemsPerRow <= 0) return;
if (maxLevels <= 0) return;
int width = (int)G.VisibleClipBounds.Width / (maxItemsPerRow + 2);
int height = (int)G.VisibleClipBounds.Height / (maxLevels + 2);
int side = width / 4;
int textOffsetX = 3;
int textOffsetY = 5;
int graphOffsetY = 50;
Size squaresize = new Size(side * 2, side * 2);
foreach (SBBTreeNode<string> node in flatTree)
{
Point P0 = new Point(node.Col * width, node.Row * height + graphOffsetY);
Point textPt = new Point(node.Col * width + textOffsetX,
node.Row * height + textOffsetY + graphOffsetY);
Point midPt = new Point(node.Col * width + side,
node.Row * height + side + graphOffsetY);
if (node.Left != null)
G.DrawLine(Pens.Black, midPt,
new Point(node.Left.Col * width + side,
node.Left.Row * height + side + graphOffsetY));
if (node.Right != null)
G.DrawLine(Pens.Black, midPt,
new Point(node.Right.Col * width + side,
node.Right.Row * height + side + graphOffsetY));
G.FillEllipse(Brushes.Beige, new Rectangle(P0, squaresize));
G.DrawString(node.Data, Font, Brushes.Black, textPt);
G.DrawEllipse(Pens.Black, new Rectangle(P0, squaresize));
}
}
and its result:
Usage:
flatTree = FlatTree();
setRows();
setCols();
panel_tree.Invalidate();
Now for the various pieces that lead up to this:
The drawTree routine is obviously triggered from a Panel's Paint event.
I uses a few class level variables:
This is the Tree I build in my tests; please note that to make things a little simpler I have dumped your generic type T for string:
Dictionary<string, SBBTreeNode<string> > tree
= new Dictionary<string, SBBTreeNode<string>>();
This is a flat traversal copy of the tree, that is, its elements are ordered by level and from left to right:
List<SBBTreeNode<string>> flatTree = new List<SBBTreeNode<string>>() ;
Here are the dimesions of the tree:
int maxItemsPerRow = 0;
int maxLevels = 0;
This is how the flat tree is created, using a Queue:
List<SBBTreeNode<string>> FlatTree()
{
List<SBBTreeNode<string>> flatTree = new List<SBBTreeNode<string>>();
Queue<SBBTreeNode<string>> queue = new Queue<SBBTreeNode<string>>();
queue.Enqueue((SBBTreeNode<string>)(tree[tree.Keys.First()]));
flatNode(queue, flatTree);
return flatTree;
}
This is the recursive call to get the nodes in order:
void flatNode(Queue<SBBTreeNode<string>> queue, List<SBBTreeNode<string>>flatTree)
{
if (queue.Count == 0) return;
SBBTreeNode<string> node = queue.Dequeue();
if (!node.IsHorizontal) flatTree.Add(node);
if (node.Left != null) { queue.Enqueue(node.Left); }
if (node.Left != null && node.Left.Right != null && node.Left.Right.IsHorizontal)
queue.Enqueue(node.Left.Right);
if (node.Right != null)
{
if (node.Right.IsHorizontal) flatTree.Add(node.Right);
else queue.Enqueue(node.Right);
}
flatNode(queue, flatTree);
}
Finally we can set the (virtual) coordinates of each node:
void setCols()
{
List<SBBTreeNode<string>> FT = flatTree;
int levelMax = FT.Last().Row;
int LMaxCount = FT.Count(n => n.Row == levelMax);
int LMaxCount1 = FT.Count(n => n.Row == levelMax-1);
if (LMaxCount1 > LMaxCount)
{ LMaxCount = LMaxCount1; levelMax = levelMax - 1; }
int c = 1;
foreach (SBBTreeNode<string> node in FT) if (node.Row == levelMax)
{
node.Col = ++c;
if (node.Left != null) node.Left.Col = c - 1;
if (node.Right != null) node.Right.Col = c + 1;
}
List<SBBTreeNode<string>> Exceptions = new List<SBBTreeNode<string>>();
for (int n = FT.Count- 1; n >= 0; n--)
{
SBBTreeNode<string> node = FT[n];
if (node.Row < levelMax)
{
if (node.IsHorizontal) node.Col = node.Left.Col + 1;
else if ((node.Left == null) | (node.Right == null)) {Exceptions.Add(node);}
else node.Col = (node.Left.Col + node.Right.Col) / 2;
}
}
// partially filled nodes will need extra attention
foreach (SBBTreeNode<string> node in Exceptions)
textBox1.Text += "\r\n >>>" + node.Data;
maxLevels = levelMax;
maxItemsPerRow = LMaxCount;
}
Note that I have not coded the special case of partially filled nodes but only add them to a list of exceptions; you have to decide what to do with those, ie if they can happen and where they ought to be painted.
OK, this is almost it. We have to do two more things:
I have taken the liberty to add two coordinate fields to your node class:
public int Row { get; set; }
public int Col { get; set; }
And I have writen my AddNode routine in such a way that the level of each node is set right there.
You will certainly want/need to do it differently. A simple SetRows routine is a snap, especially when you use the flatTree for transversal:
void setRows()
{
foreach (SBBTreeNode<string> node in flatTree)
{
if (node.Left != null) node.Left.Row = node.Row + 1;
if (node.Right != null) node.Right.Row =
node.Row + 1 - (node.Right.IsHorizontal ? 1:0);
}
}
Explanation:
Besides the flatTree, I use for drawing, the core of the solution is the SetCols routine.
In a balanced B-Tree the maximum width is reached either at the last or the second-to-last row.
Here I count the number of nodes in that row. This gives me the width of the whole tree, maxItemsPerRow. The routine also sets the height as maxLevels
Now I first set the Col values in that widest row, from left to right (and if present to dangling children in the last row.)
Then I move up level by level and calculate each Col value as the middle between the Left and Right Child, always watching out for horizontal nodes.
Note that I assume that all horizontal nodes are right children! If that is not true you will have to make various adaptions in both the FlatTree and the setCol routines..
I would start by placing the root node at (0,0) (really doesn't matter where you start). Call this point (parent_X, parent_Y). Then pick a starting width (say 2^(number of levels in your tree), if you know how many levels your tree has, otherwise, just pick any width).
The left child goes at position (parent_X-width/2, parent_Y-1) and the right child goes at position (parent_X+width/2, parent_Y-1). Then change the width to width = width/2. If a child happens to be horizontal, you can just forget the parent_Y-1 part and keep the parent_Y. Then just repeat on each of the children of the head node. Each time you move down a level, replace width with width/2 - epsilon.
Hope this helps.

Is this implementation of a Red Black Tree C# correct?

Please critique my code. I noticed my last assert fails with value 277. I expected the value to 255 (1/2 500+10). Is this a valid test or have I done something wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace RedBlackTree
{
public enum NodeColor
{
Red,
Black
}
public enum NodeSide
{
Left,
Right,
None
}
public class RedBlackTreeNode<T>
where T : IComparable<T>
{
public RedBlackTreeNode<T> Left { get; set; }
public RedBlackTreeNode<T> Right { get; set; }
public RedBlackTreeNode<T> Parent { get; set; }
public T Data {get; set;}
public NodeColor Color { get; set; }
public RedBlackTreeNode<T> Uncle
{
get
{
if (WhichSideAmIOn() == NodeSide.Left)
{
return this.Parent.Right;
}
else
return this.Parent.Left;
}
}
public string ToString()
{
return String.Format("Me: {0} Left: {1} Right: {2}", Data, Left != null ? Left.Data.ToString() : "null", Right != null ? Right.Data.ToString() : "null");
}
public NodeSide WhichSideAmIOn()
{
if (this.Parent == null) return NodeSide.None;
if (this.Parent.Left == this)
return NodeSide.Left;
if (this.Parent.Right == this)
return NodeSide.Right;
throw new Exception("Impossible - there can be only two sides. You must not be a child of your parent.");
}
}
public class RedBlackTree<T>
where T : IComparable<T>
{
private RedBlackTreeNode<T> Root { get; set; }
public void InsertNode(T data)
{
//make a node to hold the data - always red
RedBlackTreeNode<T> newNode = new RedBlackTreeNode<T>();
newNode.Data = data;
newNode.Color = NodeColor.Red;
//rule 1 - if the root is null, then hte new node is the root and roots can't be red.
if (Root == null)
{
Root = newNode;
Root.Color = NodeColor.Black;
return;
}
//otherwise if we're not the first node, insert by walking.
RedBlackTreeNode<T> walker = Root;
while (walker != null)
{
if (newNode.Data.CompareTo(walker.Data)< 0)
{
//walk left
if (walker.Left == null)
{
walker.Left = newNode;
newNode.Parent = walker;
break;
}
else
{
walker = walker.Left;
}
}
else if (newNode.Data.CompareTo(walker.Data) > 0)
{
//walk right
if (walker.Right == null)
{
walker.Right = newNode;
newNode.Parent = walker;
break;
}
else
{
walker = walker.Right;
}
}
else //todo: remove me
{
//we're equal, ignore this node in general
return;
}
}
//rebalance -
//at this point we have the parent , we have the newnode and we need to implement some rules.
Rebalance();
}
private void Rebalance()
{
RedBlackTreeNode<T> node = Root;
Stack<RedBlackTreeNode<T>> stack = new Stack<RedBlackTreeNode<T>>();
while (stack.Count !=0 || node !=null )
{
if (node != null)
{
stack.Push(node);
node = node.Left;
}
else
{
node = stack.Pop();
Rebalance(node);
node = node.Right;
}
}
}
private void Rebalance(RedBlackTreeNode<T> node)
{
if (node.Parent == null) return;
if (node.Parent.Color == NodeColor.Red) //rule 2 or 3
{
if (node.Uncle != null) //the rule 2 - change him to black as well
{
Rule2(node);
}
else //if my uncle doesn't exist, it's could be rule 3 or 4, which requires rotation
{
//if my parent and I are on the same side,
if (node.WhichSideAmIOn() == node.Parent.WhichSideAmIOn())
{
Rule3(node);
}
else
{
Rule4(node);
}
}
}
}
private void Rule2(RedBlackTreeNode<T> node)
{
//my parent + uncle needs to be black
if (node.Parent == null) throw new Exception("um how?");
node.Parent.Color = NodeColor.Black;
node.Uncle.Color = NodeColor.Black;
}
//The rule of two red nodes to the same side
//if the nodes of the tree are stacked in one direction and the two stacked nodes are red
//the middle node comes up to the parent and the top node becomes the left or right hand child.
private void Rule3(RedBlackTreeNode<T> node)
{
//make my grand parent, my parents left|right
//where am i?
NodeSide ns = node.WhichSideAmIOn();
if (node.Parent == null) throw new Exception("um how?");
RedBlackTreeNode<T> parent = node.Parent;
RedBlackTreeNode<T> grandParent = parent.Parent;
RedBlackTreeNode<T> greatGrandParent = grandParent.Parent;
//set my great, grand parent, to point at my parent
NodeSide gpSide = grandParent.WhichSideAmIOn();
if (gpSide == NodeSide.Left)
{
if (greatGrandParent !=null)
greatGrandParent.Left = parent;
}
else
{
if (greatGrandParent != null)
greatGrandParent.Right = parent;
}
//swap my grandparent into my parent's other child
if (ns == NodeSide.Left)
{
//set my parents right to my grandParent
parent.Right = grandParent;
grandParent.Left = null;
}
else if (ns == NodeSide.Right)
{
//set my parents right to my grandParent
parent.Left = grandParent;
grandParent.Right = null;
}
//reset the parent, update the root
parent.Parent = greatGrandParent;
if (greatGrandParent == null)
{
Root = parent;
}
grandParent.Parent = parent;
//swap colors
parent.Color = NodeColor.Black;
grandParent.Color = NodeColor.Red;
}
//The rule of two red nodes on different sides
//if the nodes of a tree are both red and one goes to the left, but the other goes to the right
//then the middle node becomes the parent and the top node becomes the left or right child
private void Rule4(RedBlackTreeNode<T> node)
{
if (node.Parent == null) throw new Exception("um how?");
RedBlackTreeNode<T> parent = node.Parent;
RedBlackTreeNode<T> grandParent = parent.Parent;
RedBlackTreeNode<T> greatGrandParent = grandParent.Parent;
//fix the reference that will be above me
NodeSide ns;
if (grandParent!= null)
{
ns = grandParent.WhichSideAmIOn();
//replace the reference to my grand parent with me
if (ns == NodeSide.Left)
{
greatGrandParent.Left = node;
}
else if (ns == NodeSide.Right)
{
greatGrandParent.Right = node;
}
}
//put my parent and my grand parent on the
//correct side of me.
ns = node.WhichSideAmIOn();
NodeSide parentSide = parent.WhichSideAmIOn();
if (ns == NodeSide.Left)
{
node.Left = grandParent;
node.Right = parent;
//I was the child of parent, wipe this refernce
parent.Left = null;
}
else
{
node.Left = parent;
node.Right = grandParent;
//i was the child of parent, wipe this reference
parent.Right = null;
}
parent.Parent = node;
grandParent.Parent = node;
//parent was the child of grandparent, wipe this reference
if (parentSide == NodeSide.Left) { grandParent.Left = null; }
if (parentSide == NodeSide.Right) { grandParent.Right = null; }
//reset my parent and root
node.Parent = greatGrandParent;
if (greatGrandParent == null)
{
Root = node;
}
//swap colors
node.Color = NodeColor.Black;
grandParent.Color = NodeColor.Red;
}
public void Print()
{
Stack<RedBlackTreeNode<T>> stack = new Stack<RedBlackTreeNode<T>>();
RedBlackTreeNode<T> temp = Root;
while (stack.Count != 0 || temp != null)
{
if (temp != null)
{
stack.Push(temp);
temp = temp.Left;
}
else
{
temp = stack.Pop();
Console.WriteLine(temp.Data.ToString());
temp = temp.Right;
}
}
}
public double Height
{
get
{
Stack<RedBlackTreeNode<T>> stack = new Stack<RedBlackTreeNode<T>>();
RedBlackTreeNode<T> temp = Root;
double currentHeight =0;
while (stack.Count != 0 || temp != null)
{
if (temp != null)
{
stack.Push(temp);
if (temp.Left != null || temp.Right != null)
{
currentHeight++;
}
temp = temp.Left;
}
else
{
temp = stack.Pop();
temp = temp.Right;
}
}
return currentHeight;
}
}
}
class Program
{
static void Main(string[] args)
{
RedBlackTree<int> rbt = new RedBlackTree<int>();
rbt.InsertNode(1);
rbt.InsertNode(2);
rbt.InsertNode(3);
rbt.InsertNode(4);
rbt.InsertNode(5);
rbt.InsertNode(6);
rbt.InsertNode(7);
rbt.InsertNode(8);
rbt.InsertNode(9);
rbt.InsertNode(10);
rbt.Print();
Assert.AreEqual(5, rbt.Height); //make sure sorted vals don't snake off to the left or right
//inert 500 more random numbers, height should remain balanced
Random random = new Random();
for (int i = 0; i < 500; i++)
{
rbt.InsertNode(random.Next(0, 10000));
}
Assert.AreEqual(255, rbt.Height);
}
}
}
I think your test is incorrect, although I think your code has other problems that the test isn't catching.
First of all, the Height property does not actually return the height, but the number of nodes with at least one child. If you want the height of the deepest node then you should do something like currentHeight = Math.Max(currentHeight, stack.Count) on each iteration instead. You may also want it to return an int rather than a double.
The number of nodes without children should be approximately half of them like you want, but red-black trees are not perfectly balanced. You can have a valid tree with one third of the nodes having one child, one third having two, and one third having none: start with a perfectly balanced tree with all black nodes at the last level and add a red child to each one. This maintains the red-black tree invariants, but as many as two-thirds of the nodes will have children.
Similarly, if you were to test depth it would be between log(N) and 2 log(N).
You may want to write tests that verify the invariants of the tree directly. Visit every node in the tree, and verify that every red node has a black parent and that every path to a leaf contains the same number of black nodes. If you run those tests after every insert in your test suite, you can be sure that the tree is always balanced.
As for the code itself, your Rebalance method crawls the entire tree on every insert. This means insert will require O(N) time and will negate the benefits of using a self-balancing tree. Retrieval will still be O(log N), but you could get the same result by keeping a sorted list and inserting elements into the appropriate place. You should only have to rebalance the tree along the path being inserted, which will only be O(log N) nodes.
I think some of your transformations are wrong. You don't check the color of the current node before calling Rule2, and that rule appears to change nodes to black without ensuring that other paths in the tree have the same number of black nodes. (I may be misreading it; red-black trees are too complicated to do entirely in my head.)
If you're looking for a reference implementation, the Wikipedia page on Red-black trees has an implementation in C that could easily be translated to C#, and SortedSet<T> is implemented using a red-black tree that you can view with Reflector.

Categories

Resources