I’ve written an implementation of A* that relies on sorting nodes by their F score in a sortedSet.
The sorting, in some cases, seems to insert a Node object at the 'Min' value when its compared 'F' value is actually the second lowest rather than the Min, as described. I'm completely baffled as to why this is happening. I believe it's causing the knock-on effect of causing nodeTree.Remove and nodeTree.RemoveWhere to fail, but that might be the actual cause of the issue, I'm honestly not sure - though I wouldn't know how to fix it if it is.
This is the comparer used. I assume it's relatively obvious that I'm not exactly sure how to implement these, but I think this should work as I intend.
public class FValueFirst : Comparer<PathfindingAgent.Node>
{
public override int Compare(PathfindingAgent.Node x, PathfindingAgent.Node y)
{
int result = x.F.CompareTo(y.F);
if (result == 0)
{
result = y.G.CompareTo(x.G);
}
if(x == y)
{
result = 0;
}
return result;
}
}
This is the Node object, for reference.
public class Node
{
public Cell cell;
public float G;
public float H;
public bool Opened;
public bool Closed;
public Node Previous;
public float F { get => G + H; }
}
This is the function it all occurs in. The result is deterministic, thankfully. Depending on the current destID and the particular layout of the grid's obstacles it will always get out of sort on the same iteration.
public void PathTo(Vector3Int destID)
{
SortedSet<Node> nodeTree = new SortedSet<Node>(new FValueFirst());
Vector3Int radius = PathfindingGrid.Instance.GridRadius;
NodeGrid = new Node[radius.x * 2 + 1, radius.y * 2 + 1, radius.z * 2 + 1];
Node startNode = new Node()
{
cell = PathfindingGrid.Cells[CurrentID.x, CurrentID.y, CurrentID.z],
G = 0,
H = 0
};
Node endNode = new Node()
{
cell = PathfindingGrid.Cells[destID.x, destID.y, destID.z],
G = 0,
H = 0
};
Vector3Int sID = startNode.cell.ID;
Vector3Int eID = endNode.cell.ID;
NodeGrid[sID.x, sID.y, sID.z] = startNode;
NodeGrid[eID.x, eID.y, eID.z] = endNode;
if (endNode.cell.IsOccupied) return;
nodeTree.Add(startNode);
int iterations = 0;
while(true)
{
Node node;
node = nodeTree.Min;
node.Closed = true;
nodeTree.RemoveWhere(n => n == node);
if(node == nodeTree.Min)
{
throw new Exception($"Incorrect node was removed from the tree");
}
if (node == endNode)
{
List<Node> chain = BacktraceChain(node);
Debug.Log($"Path found from {CurrentID} to {destID} with score {endNode.G} traversing {chain.Count} cells in {iterations} iterations");
DrawLine(chain, Color.white);
break;
}
List<Node> neighbours = GetNeighbours(node);
foreach(Node neighbour in neighbours)
{
if (neighbour == startNode || neighbour.Closed) continue;
float newg = Vector3Int.Distance(node.cell.ID, neighbour.cell.ID) + node.G;
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
}
}
iterations++;
}
}
For posterity, I solved the issue - it was due to my inexperience with the SortedList type.
This code, found near the end of the function was to blame
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
Specifically, an item in a tree cannot have its compared values modified to the point where it no longer compares correctly in that index. The item must first be removed from the list, modified, and readded.
My guess in hindsight is that, though removed immediately after modification, the tree is unable to be sufficiently traversed to access the target item due to the modification.
Thus my solution was to simply re-arrange the block so that the removal and addition occured on either side of the modification respectively, like so:
if (!neighbour.Opened || newg < neighbour.G)
{
if (neighbour.Opened)
{
if (!nodeTree.Remove(neighbour)) throw new Exception($"{neighbour} was not removed from tree");
}
else
{
neighbour.Opened = true;
}
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
nodeTree.Add(neighbour);
}
Related
I'm working on SPOJ problem where you have to write an algorithm that based on input string conditions outputs new string, but you can't exceede time limit.
problem link
The fastest i could get was by using two stacks, but time limit was still exceeded, now I tried implementing doubly linked list, but it's twice slower than when I used stack. Do you have any idea on how can I increase performance of implemented linked list, or maybe should I use other data structure for this problem? Thought of implementing Node as a structure but not really sure if you can do that.
using System;
namespace spoj
{
class LinkedList
{
private Node head;
private Node tail;
private int length;
public Node Head { get => head; }
public Node Tail { get => tail; }
public int Length { get => length; }
public LinkedList(Node head = null, Node tail = null, int length = 0)
{
this.head = head;
this.tail = tail;
this.length = length;
}
public void AddFirst(char value)
{
var addFirst = new Node(value);
addFirst.Next = head;
addFirst.Previous = null;
if (head != null)
head.Previous = addFirst;
head = addFirst;
length++;
}
public void Remove(Node node)
{
if (node.Previous == null)
{
head = node.Next;
head.Previous = null;
length--;
}
else if (node.Next == null)
{
tail = node.Previous;
tail.Next = null;
length--;
}
else
{
Node temp1 = node.Previous;
Node temp2 = node.Next;
temp1.Next = temp2;
temp2.Previous = temp1;
length--;
}
}
public void AddAfter(Node node, char input)
{
var newNode = new Node(input);
if (node.Next == null)
{
node.Next = newNode;
newNode.Previous = node;
length++;
}
else
{
Node temp1 = node;
Node temp2 = node.Next;
temp1.Next = newNode;
newNode.Previous = temp1;
newNode.Next = temp2;
temp2.Previous = newNode;
length++;
}
}
public string Print()
{
string temp = "";
if (head == null)
return temp;
for (int i = 0; i < length; i++)
{
temp += head.Value;
head = head.Next;
}
return temp;
}
}
class Node
{
private char value;
private Node next;
private Node previous;
public char Value { get => value; }
public Node Next { get => next; set { next = value; } }
public Node Previous { get => previous; set { previous = value; } }
public Node(char value)
{
this.value = value;
next = null;
previous = null;
}
}
class Program
{
static void Main(string[] args)
{
int testNum = Int32.Parse(Console.ReadLine());
for (int i = 0; i < testNum; i++)
{
var list = new LinkedList();
string input = Console.ReadLine();
var node = list.Head;
for (int j = 0; j < input.Length; j++)
{
if ((input[j] == '<' && node == null) | (input[j] == '>' && (node == null || node.Next == null)) | (input[j] == '-' && (node == null || node.Previous == null)))
continue;
else if (input[j] == '<')
{
node = node.Previous;
}
else if (input[j] == '>')
{
node = node.Next;
}
else if (input[j] == '-')
{
node = node.Previous;
list.Remove(node.Next);
}
else
{
if (node == null)
{
list.AddFirst(input[j]);
node = list.Head;
continue;
}
list.AddAfter(node, input[j]);
node = node.Next;
}
}
Console.WriteLine(list.Print());
}
}
}
}
An implementation using a linked list will not be as fast as one that uses StringBuilder, but assuming you are asking about a linked list based implementation I would suggest not to reimplement LinkedList. Just use the native one.
This means you don't have to change much in your code, just this:
Define the type of the list nodes as char: new LinkedList<char>();
Instead of .Head use .First
Instead of .Print use string.Join("", list)
However, there are these problems in your code:
When the input is >, you should allow the logic to execute when node is null. Currently you continue, but a null may mean that your "cursor" is in front of the non-empty list, so you should still deal with it, and move the "cursor" to list.First
When the input is -, you should still perform the removal even when node.Previous is null, because it is not the previous node that gets removed, but the current node. We should imagine the cursor to be between two consecutive nodes, and your removal logic shows that you took as rule that the cursor is between the current node and node.Next. You could also have taken another approach (with the cursor is just before node), but important is that all your logic is consistent with this choice.
When executing the logic for - -- in line with the previous point -- you should take into account that node.Previous could be null, and in that case you cannot do the removal as you have it. Instead, you could first assign the node reference to a temporary variable, then move the cursor, and then delete the node that is referenced by the temporary reference.
Here is the corrected code, using the native LinkedList implementation. I moved the logic for doing nothing (your continue) inside each separate case, as I find that easier to understand/debug:
using System;
using System.Collections.Generic;
public class Test
{
public static void Main()
{
int testNum = Int32.Parse(Console.ReadLine());
for (int i = 0; i < testNum; i++)
{
var list = new LinkedList<char>();
string input = Console.ReadLine();
var node = list.First;
for (int j = 0; j < input.Length; j++)
{
if (input[j] == '<')
{
if (node != null)
node = node.Previous;
}
else if (input[j] == '>')
{
if (node == null || node.Next != null)
node = node == null ? list.First : node.Next;
}
else if (input[j] == '-')
{
if (node != null) {
var temp = node;
node = node.Previous;
list.Remove(temp);
}
}
else
{
node = node == null ? list.AddFirst(input[j])
: list.AddAfter(node, input[j]);
}
}
Console.WriteLine(string.Join("", list));
}
}
}
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.
I need some help implementing Dijkstra's Algorithm and was hoping someone would be able to assist me. I have it so that it is printing some of routes but it isn't capturing the correct costs for the path.
Here is my node structure:
class Node
{
public enum Color {White, Gray, Black};
public string Name { get; set; } //city
public List<NeighborNode> Neighbors { get; set; } //Connected Edges
public Color nodeColor = Color.White;
public int timeDiscover { get; set; }//discover time
public int timeFinish { get; set; } // finish time
public Node()
{
Neighbors = new List<NeighborNode>();
}
public Node(string n, int discover)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
timeDiscover = discover;
}
public Node(string n, NeighborNode e, decimal m)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
this.Neighbors.Add(e);
}
}
class NeighborNode
{
public Node Name { get; set; }
public decimal Miles { get; set; } //Track the miles on the neighbor node
public NeighborNode() { }
public NeighborNode(Node n, decimal m)
{
Name = n;
Miles = m;
}
}
Here is my algorithm:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
Stack<Node> _allCities = new Stack<Node>(); // add all cities into this for examination
Node _nodeToExamine = new Node(); //this is the node we're currently looking at.
decimal _cost = 0;
foreach (var city in graph) // putting these onto a stack for easy manipulation. Probably could have just made this a stack to start
{
_allCities.Push(city);
_algorithmList.Add(new DA(city));
}
_nodeToExamine = _allCities.Pop(); //pop off the first node
while (_allCities.Count != 0) // loop through each city
{
foreach (var neighbor in _nodeToExamine.Neighbors) //loop through each neighbor of the node
{
for (int i = 0; i < _algorithmList.Count; i++) //search the alorithm list for the current neighbor node
{
if (_algorithmList[i].Name.Name == neighbor.Name.Name) //found it
{
for (int j = 0; j < _algorithmList.Count; j++) //check for the cost of the parent node
{
if (_algorithmList[j].Name.Name == _nodeToExamine.Name) //looping through
{
if (_algorithmList[j].Cost != 100000000) //not infinity
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
break;
}
}
_cost = _cost + neighbor.Miles;
if (_algorithmList[i].Cost > _cost) // check to make sure the miles are less (better path)
{
_algorithmList[i].Parent = _nodeToExamine; //set the parent to be the top node
_algorithmList[i].Cost = _cost; // set the weight to be correct
break;
}
}
}
}
_cost = 0;
_nodeToExamine = _allCities.Pop();
}
}
This is what the graph looks like:
The graph list node is essentially
Node -- Neighbor Nodes
So for example:
Node = Olympia, Neighbor Nodes = Lacey and Tacoma
I think the problem is that
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
You do a direct assignment of cost, instead of an addition of old and new cost.
Also, the fact that you do
if (_algorithmList[j].Cost != 100000000) //not infinity
directly before it means that if the cost of the path is infinity, you do the very opposite - you add zero to the cost of the path, making it the least expensive instead of most expensive path.
If you want to check for infinity properly, you have to outright skip taking that path when you inspect its cost, not just skip calculating the cost.
I needed to rewrite the entire algorithm as it wasn't processing correctly:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
DA _nodeToExamine = new DA(); //this is the node we're currently looking at.
bool flag = true; //for exting the while loop later
foreach (var node in graph)
{
_algorithmList.Add(new DA(node));
}
foreach (var children in _algorithmList[0].Name.Neighbors) //just starting at the first node
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
_algorithmList[i].Parent = _algorithmList[0].Name;
_algorithmList[i].Cost = children.Miles;
_algorithmList[0].Complete = true;
}
}
}
while (flag) //loop through the rest to organize
{
_algorithmList = _algorithmList.OrderBy(x => x.Cost).ToList(); //sort by shortest path
for (int i = 0; i < _algorithmList.Count; i++) //loop through each looking for a node that isn't complete
{
if (_algorithmList[i].Complete == false)
{
_nodeToExamine = _algorithmList[i];
break;
}
if (i == 13) //if the counter reaches 13 then we have completed all nodes and should bail out of the loop
flag = false;
}
if (_nodeToExamine.Name.Neighbors.Count == 0) //set any nodes that do not have children to be complete
{
_nodeToExamine.Complete = true;
}
foreach (var children in _nodeToExamine.Name.Neighbors) //loop through the children/neighbors to see if there's one with a shorter path
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
if (_nodeToExamine.Cost + children.Miles < _algorithmList[i].Cost) //found a better path
{
_algorithmList[i].Parent = _nodeToExamine.Name;
_algorithmList[i].Cost = _nodeToExamine.Cost + children.Miles;
}
}
}
_nodeToExamine.Complete = true;
}
}
PrintDijkstraAlgoirthm(_algorithmList);
}
public void PrintDijkstraAlgoirthm(List<DA> _finalList)
{
foreach (var item in _finalList)
{
if (item.Parent != null)
Console.WriteLine("{0} ---> {1}: {2}", item.Parent.Name, item.Name.Name, item.Cost);
}
}
I have a class with the following definition,
class BinomialNode
{
public int key; // The key value
public int x_point; // x co-ordinate for drawing
public int y_point; // y co-ordinate for drawing
public int degree; // number of siblings/children for current node
public BinomialNode parent;
public BinomialNode child;
public BinomialNode sibling;
...
}
We are learning Binomial Heaps in college and I've implemented the merge and insert algorithms in code. At least, when I pause Visual Studio and go over the "Locals" (by hovering the mouse over a variable), I see the data as I expect.
As an experiment, I've added 2 extra variables to the standard "Binomial Node". They are x_point and y_point. Now during program execution I see this,
Please note the area I've indicated above. It's supposed to represent the same node, but as we can see, the value of x_point is different. (In other cases, y_point is different)
Does anyone have a clue why this is happening? As I understand things, if it represents the same node, the data should be identical. But it isn't - which means it's not the same node. How can that be possible? If I ignore my "extra" x_point and y_point variables, the code runs perfectly! In fact, I wouldn't have even know this to be a problem.
It's not visible from my snippet, but x_point & y_point are the only values I EDIT outside the class definition. The others, while public are only read from.
EDIT:
Here is the code I've made,
class BinomialNode
{
public int key; // The key value
public int x_point; // x co-ordinate for drawing
public int y_point; // y co-ordinate for drawing
public int degree; // number of siblings/children for current node
public BinomialNode parent;
public BinomialNode child;
public BinomialNode sibling;
// Binomial Link takes the tree rooted at y and makes it a child of z
private static void Binomial_Link(ref BinomialNode y,ref BinomialNode z)
{
y.parent = z;
y.sibling = z.child;
z.child = y;
z.degree++;
}
// This merges the root lists of H1 and H2 into a single linked list that is sorted
// by degree in increasing order
private static BinomialNode Binomial_Heap_Merge(BinomialNode H1, BinomialNode H2)
{
BinomialNode H = new BinomialNode();
BinomialNode temp = H;
if (H1 == null) // if it's the first insert
{
return H2;
}
while (H1 != null && H2 != null)
{
if (H1.degree < H2.degree)
{
// insert H1 into position
temp.key = H1.key;
temp.x_point = H1.x_point;
temp.y_point = H1.y_point;
temp.child = H1.child;
temp.degree = H1.degree;
temp.sibling = new BinomialNode();
temp = temp.sibling;
// move H1 to the next sibling
H1 = H1.sibling;
}
else
{
// insert H2 into position
temp.key = H2.key;
temp.x_point = H2.x_point;
temp.y_point = H2.y_point;
temp.child = H2.child;
temp.degree = H2.degree;
temp.sibling = new BinomialNode();
temp = temp.sibling;
// move H2 to next sibling
H2 = H2.sibling;
}
}
// one of them hit null, so fill in the rest
while (H1 != null)
{
// insert H1 into position
temp.key = H1.key;
temp.x_point = H1.x_point;
temp.y_point = H1.y_point;
temp.child = H1.child;
temp.degree = H1.degree;
temp.sibling = new BinomialNode();
temp = temp.sibling;
// move H1 to the next sibling
H1 = H1.sibling;
}
while (H2 != null)
{
// insert H2 into position
temp.key = H2.key;
temp.x_point = H2.x_point;
temp.y_point = H2.y_point;
temp.child = H2.child;
temp.degree = H2.degree;
temp.sibling = new BinomialNode();
temp = temp.sibling;
// move H2 to the next sibling
H2 = H2.sibling;
}
// To remove the extra node added,
temp = H;
while (temp != null)
{
if (temp.sibling.key == 0 && temp.sibling.sibling == null && temp.sibling.child == null)
{
// found the extra, now to get rid of it!
temp.sibling = null;
}
temp = temp.sibling;
}
return H; // send back the merged heap
}
// Unites the binomial heaps H1 & H2 and returns resulting heap
public static BinomialNode Binomial_Heap_Union(BinomialNode H1, BinomialNode H2)
{
BinomialNode prev_x, x, next_x;
BinomialNode H = new BinomialNode();
H = Binomial_Heap_Merge(H1, H2);
// simple checks
if (H == null)
{
return H;
}
else
{
prev_x = null;
x = H;
next_x = x.sibling;
}
// now, for the actual merging
while (next_x != null)
{
if ((x.degree != next_x.degree) || (next_x.sibling != null && x.degree == next_x.sibling.degree))
{
prev_x = x;
x = next_x;
}
else if (x.key <= next_x.key)
{
x.sibling = next_x.sibling;
Binomial_Link(ref next_x, ref x);
}
else
{
if (prev_x == null)
{
H = next_x;
}
else
{
prev_x.sibling = x.sibling;
}
Binomial_Link(ref x, ref next_x);
x = next_x;
}
next_x = x.sibling;
}
// now, to return the merged heap
return H;
}
// inserting a key into a heap
public static void Binomial_Heap_Insert(ref BinomialNode H, int x)
{
BinomialNode H_temp = new BinomialNode();
H_temp.key = x;
H_temp.parent = null;
H_temp.degree = 0;
H_temp.sibling = null;
H_temp.child = null;
H = Binomial_Heap_Union(H, H_temp);
}
}
I use a form window to get the data from users to fill in the heap. The input is here,
private void button1_Click(object sender, EventArgs e)
{
BinomialNode.Binomial_Heap_Insert(ref B_HEAP1, Int32.Parse(numericUpDown1.Value.ToString()));
// drawing the heap
pictureBox1.Refresh();
int x = 0;
DrawNodeValue(pictureBox1, ref B_HEAP1, ref x, 0);
}
I hope the code isn't too badly made?
You're creating a new node, and then copying all the values over. Is that what you want to do? If you expect to be using the same nodes, then use the same nodes.
Instead of:
Node H = new Node();
Node temp = H;
if(node1 > node2)
temp.values = node1.values
else
temp.values = node2.values
Just use the actual objects...
Node temp;
if(node1 > node2)
temp = node1;
else
temp = node2;
I'm not sure where the values are getting separated, but this is why they're not actually the same node.
Here is the situation, I am developing a binary search tree and in each node of the tree I intend to store the height of its own for further balancing the tree during avl tree formation. Previously I had an iterative approach to calculate the height of a node during balancing the tree like the following.
(The following code belongs to a class called AVLTree<T> which is a child class of BinarySearchTree<T>)
protected virtual int GetBalance(BinaryTreeNode<T> node)
{
if(node != null)
{
IEnumerable<BinaryTreeNode<T>> leftSubtree = null, righSubtree = null;
if (node.Left != null)
leftSubtree = node.Left.ToEnumerable(BinaryTreeTraversalType.InOrder);
if (node.Right != null)
righSubtree = node.Right.ToEnumerable(BinaryTreeTraversalType.InOrder);
var leftHeight = leftSubtree.IsNullOrEmpty() ? 0 : leftSubtree.Max(x => x.Depth) - node.Depth;
var righHeight = righSubtree.IsNullOrEmpty() ? 0 : righSubtree.Max(x => x.Depth) - node.Depth;
return righHeight - leftHeight;
}
return 0;
}
But it was incurring a lot of performance overhead.
Performance of an AVL Tree in C#
So I went for storing the height value in each node at the time of insertion in the BinarySearchTree<T>. Now during balancing I am able to avoid this iteration and I am gaining the desired performance in AVLTree<T>.
But now the problem is if I try to insert a large number of data say 1-50000 sequentially in BinarySearchTree<T> (without balancing it), I am getting StackoverflowException. I am providing the code which is causing it. Can you please help me to find a solution which will avoid this exception and also not compromise with the performance in its child class AVLTree<T>?
public class BinaryTreeNode<T>
{
private BinaryTreeNode<T> _left, _right;
private int _height;
public T Value {get; set; }
public BinaryTreeNode<T> Parent;
public int Depth {get; set; }
public BinaryTreeNode()
{}
public BinaryTreeNode(T data)
{
Value = data;
}
public BinaryTreeNode<T> Left
{
get { return _left; }
set
{
_left = value;
if (_left != null)
{
_left.Depth = Depth + 1;
_left.Parent = this;
}
UpdateHeight();
}
}
public BinaryTreeNode<T> Right
{
get { return _right; }
set
{
_right = value;
if (_right != null)
{
_right.Depth = Depth + 1;
_right.Parent = this;
}
UpdateHeight();
}
}
public int Height
{
get { return _height; }
protected internal set
{
_height = value;
if (Parent != null) {
Parent.UpdateHeight();
}
}
}
private void UpdateHeight()
{
if (Left == null && Right == null) {
return;
}
if(Left != null && Right != null)
{
if (Left.Height > Right.Height)
Height = Left.Height + 1;
else
Height = Right.Height + 1;
}
else if(Left == null)
Height = Right.Height + 1;
else
Height = Left.Height + 1;
}
}
public class BinarySearchTree<T>
{
private readonly Comparer<T> _comparer = Comparer<T>.Default;
public BinarySearchTree()
{
}
public BinaryTreeNode<T> Root {get; set;}
public virtual void Add(T value)
{
var n = new BinaryTreeNode<T>(value);
int result;
BinaryTreeNode<T> current = Root, parent = null;
while (current != null)
{
result = _comparer.Compare(current.Value, value);
if (result == 0)
{
parent = current;
current = current.Left;
}
if (result > 0)
{
parent = current;
current = current.Left;
}
else if (result < 0)
{
parent = current;
current = current.Right;
}
}
if (parent == null)
Root = n;
else
{
result = _comparer.Compare(parent.Value, value);
if (result > 0)
parent.Left = n;
else
parent.Right = n;
}
}
}
I am getting the StackoverflowException in calculating the height at the following line
if (Parent != null) {
Parent.UpdateHeight();
}
in the Height property of BinaryTreeNode<T> class. If possible please suggest me some work around.
BTW, thanks a lot for your attention to read such a long question :)
When you add a node you compute the height by iterating over all the parent nodes recursively. A .NET process has limited stack space and given a big tree you will consume all stack space and get a StackOverflowException. You can change the recursion into an iteration to avoid consuming stack space. Other languages like functional languages are able to recurse without consuming stack space by using a technique called tail recursion. However, in C# you will have to manually modify your code.
Here are modified versions of Height and UpdateHeight in BinaryTreeNode<T> that doesn't use recursion:
public int Height {
get { return _height; }
private set { _height = value; }
}
void UpdateHeight() {
var leftHeight = Left != null ? Left.Height + 1 : 0;
var rightHeight = Right != null ? Right.Height + 1 : 0;
var height = Math.Max(leftHeight, rightHeight);
var node = this;
while (node != null) {
node.Height = height;
height += 1;
node = node.Parent;
}
}
You can add a tail. call in il, decompile the file and then compile it again.
Example:
.... IL_0002: add
tail.
IL_0003: call ...
IL_0008: ret
Example on compiling it again:
ilasm C:\test.il /out=C:\TestTail.exe
(this is probably not what you want, but again it's just an example)
I'm sure you can figure it out and make it work, it's not to hard.
The big downside is that recompilation will get rid of your tail call so I recommend to set up a build task in msbuild to do it automatically for you.
I think I found the solution, I modified the code as follows and it worked like a charm
public int Height
{
get { return _height; }
protected internal set
{
_height = value;
}
}
private void UpdateHeight()
{
if (Left == null && Right == null) {
return;
}
if(Left != null && Right != null)
{
if (Left.Height > Right.Height)
Height = Left.Height + 1;
else
Height = Right.Height + 1;
}
else if(Left == null)
Height = Right.Height + 1;
else
Height = Left.Height + 1;
var parent = Parent;
while (parent != null) {
parent.Height++;
parent = parent.Parent;
}
}
Thanks a lot guys who spend some time for me to tried to find out the solution.
If you are inserting large amounts of data in one go I would think you'd be better of batch inserting the data without the call to Parent.UpdateHeight then walk the tree setting the height as you go.
Adding future nodes I would walk the tree, starting at the root, incrementing the height as you go.