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.
Related
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);
}
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
Using a chart control on a windows form in C#, with visual studio 2010 IDE.
I'm creating a chart that plots random points. Once points are generated, I run a loop for each point in the chart, looking at all the other point coordinates. Inside that loop, I calculate the distance from the parent point to it's neighbor. If the distance is <= some distance that I specify, I want a line to be drawn showing a connection between the two. Problem I'm having though is actually drawing that line. Further more, I then need to find a way to walk the shortest path in that chart.
So this is really two questions:
1. How do I draw the lines on the chart? (current dilemma)
2. How do I walk the graph to find the shortest path?
Here is a snipper of some code I'm using to accomplish this, along with the current error:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void createNodes(int x, int y, int Nodes, int dgNodes)
{
Random rdn = new Random();
for (int i = 0; i < (Nodes - dgNodes); i++)
{
chtGraph.Series["Series1"].Points.AddXY
(rdn.Next(x), rdn.Next(y));
}
for (int i = 0; i <= dgNodes - 1; i++)
{
chtGraph.Series["Series2"].Points.AddXY
(rdn.Next(x), rdn.Next(y));
}
}
public void buildGraph(int x, int y, int Nodes, int dgNodes)
{
//set the min/max axis on the chart
chtGraph.ChartAreas["ChartArea1"].AxisX.Maximum = x;
chtGraph.ChartAreas["ChartArea1"].AxisX.Minimum = 0;
chtGraph.ChartAreas["ChartArea1"].AxisY.Maximum = y;
chtGraph.ChartAreas["ChartArea1"].AxisY.Minimum = 0;
chtGraph.ChartAreas["ChartArea1"].AxisX.Interval = x / 10;
chtGraph.ChartAreas["ChartArea1"].AxisY.Interval = y / 10;
chtGraph.ChartAreas["ChartArea1"].AxisX.MajorGrid.Enabled = false;
chtGraph.ChartAreas["ChartArea1"].AxisY.MajorGrid.Enabled = false;
//build all the nodes
createNodes(x, y, Nodes, dgNodes);
}
public void drawEdges(int intNumNodes, int intTransPower)
{
Pen pen = new Pen(Color.Black, 1);
Graphics g = chtGraph.CreateGraphics();
Point[] pts = new Point[intNumNodes];
int i = 0;
//Gather all the data generator data points into a point array
foreach (DataPoint p in chtGraph.Series[0].Points)
{
Point point = new Point((int)p.XValue, (int)p.YValues[0]);
pts[i] = point;
i++;
}
//Gather all the non data generator into the same point array
foreach (DataPoint p in chtGraph.Series[1].Points)
{
Point point = new Point((int)p.XValue, (int)p.YValues[0]);
pts[i] = point;
i++;
}
//loop through all the data points
foreach (Point p in pts)
{
//examine all the other data points for each data point visited
for (int j = 0; j < pts.Length; j++)
{
//if the distance from the parent node (p) to the neighbor node is less than the transmit power, then draw a line
if (Math.Sqrt(Math.Pow((p.X - pts[j].X), 2) + Math.Pow((p.Y - pts[j].Y), 2)) <= intTransPower)
{
//gr.DrawLine(pen, p, pts[j]);
//gr.Graphics.DrawLine(pen, p.X, p.Y, pts[j].X, pts[j].Y);
}
}
}
}
private void btnExecute_Click(object sender, EventArgs e)
{
if (txtDG.Text == "" || txtNodes.Text == "" || txtStorage.Text == "" || txtTransPower.Text == ""
|| txtXAxis.Text == "" || txtXAxis.Text == "")
{
lblError.Text = "Please enter in all inputs!";
lblError.Visible = true;
return;
}
//create variables for use through program
int intTransPower = Convert.ToInt32(txtTransPower.Text);
int intXAxis = Convert.ToInt32(txtXAxis.Text);
int intYAxis = Convert.ToInt32(txtYAxis.Text);
int intNum_DG = Convert.ToInt32(txtDG.Text);
int intNumNodes = Convert.ToInt32(txtNodes.Text);
int intStorage = Convert.ToInt32(txtStorage.Text);
lblError.Visible = false;
lblError.Text = "";
if (txtDG.Text == "" || txtNodes.Text == "" || txtStorage.Text == "" || txtTransPower.Text == ""
|| txtXAxis.Text == "" || txtXAxis.Text == "")
{
lblError.Text = "Please enter in all inputs!";
lblError.Visible = true;}
chtGraph.Series["Series1"].Points.Clear();
chtGraph.Series["Series2"].Points.Clear();
buildGraph(intXAxis, intYAxis, intNumNodes, intNum_DG);
drawEdges(intNumNodes, intTransPower);
}
}
Error: Error 1 The type 'System.Windows.Forms.DataVisualization.Charting.ChartGraphics' has no constructors defined
Used event described in comment in the question.
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.