我一直在寻找一种算法来将B树绘制到C#,WinForms中的用户控件。我想知道是否有一些简单的算法可用于在链接节点之间绘制线条,因为这是我的主要问题。对于绘制节点,我使用了Knuth算法,因为它很简单,只需要一次遍历遍历,我只需要一次遍历。我真的很感激任何资源,因为我只发现了一个遍历两次的东西,并且需要对节点类进行大量修改,这是我很想避免的。
我发现的唯一一件事(如上所述)是http://www.cs.unc.edu/techreports/89-034.pdf,但这根本不能说服我。
我已经是inorder遍历算法:
public void Traverse(BTree<TKey, TValue>.TraverseFunction function, ref int depth, ref int xPos)
{
depth++;
int i = 0;
for (i = 0; i < elements.Count; i++)
{
if (!isLeaf)
children[i].Traverse(function, ref depth, ref xPos);
function.Invoke(elements[i].Key, elements[i].Value, ref depth, ref xPos);
xPos++;
}
if (!isLeaf)
children[children.Count - 1].Traverse(function, ref depth, ref xPos);
depth--;
}
与TraverseFuction委托匹配的绘图方法:
private void traverseFunction(int key, string value, ref int depth, ref int xPos)
{
float x, y;
x = xPos * (horizontalSpacing + horizontalSize);
y = depth * (verticalSpacing + verticalSize);
RectangleF rect = new RectangleF(x, y, horizontalSize, verticalSize);
if(g.VisibleClipBounds.IntersectsWith(rect))
{
if(depth != treeHeight)
{
g.FillRectangle(Brushes.Black, x, y, horizontalSize, verticalSize);
g.DrawString(key.ToString(), SystemFonts.DefaultFont, Brushes.Gold, x + 2, y + 2);
}
else
{
g.FillRectangle(Brushes.Red, x, y, horizontalSize, verticalSize);
g.DrawString(key.ToString(), SystemFonts.DefaultFont, Brushes.White, x + 2, y + 2);
}
}
Console.WriteLine(key + " : " + depth);
}
它(在Paint事件中)传递给Tree traverse 方法,该方法在root上调用第一个方法。
问题是在链接节点之间画线(实际上是键)。
我可以使用全局变量(控制范围)。我希望避免几次迭代并将点 - 节点存储在另一个类中,但如果这不可能,我会“去”它。
@EDIT
我决定从这里开始使用算法:
http://billmill.org/pymag-trees/(最底层)并且我无法弄明白,我试图将其转换为C#,但我被困在
vol = vol.left()
vor = vor.right()
因为没有解释Left()和Right()做什么,我认为它们返回最近的左右兄弟,但我认为可能不是这种情况,因为它在不应该的情况下给我null(因此退出申请表。)
@EDIT 2
我把它整理出来,left()基本上是来自上面算法的nextLeft()
def nextleft(tree):
if tree.thread: return tree.thread
if tree.children: return tree.children[0]
else: return None
尽管实现了这个,我还是喜欢获得任何“更快”的算法,因为我不需要那么复杂的算法,也不会显示键,只显示节点。
答案 0 :(得分:1)
在函数式编程术语中,您必须折叠树:使用前一个节点的结果处理每个节点,作为处理输入的一部分。
因此,尝试将父节点的位置传递给它的子节点。这允许您从子节点向父节点绘制一条线,而不必遍历两次。
我更改了Traverse()
以接受Func
:
public void Traverse<TResult>(
Func<KeyValuePair<TKey, TValue>, TResult, int, int, TResult> traverser,
TResult parentResult,
ref int depth,
ref int xPos)
{
depth++;
int i = 0;
for (i = 0; i < elements.Count; i++)
{
// Traverse this node first.
var traverseResult = traverser.Invoke(elements[i], parentResult, depth, xPos);
// Now traverse children and pass result of own traversal.
if (!isLeaf)
children[i].Traverse(traverser, traverseResult, ref depth, ref xPos);
xPos++;
}
if (!isLeaf)
children[children.Count - 1].Traverse(function, traverseResult, ref depth, ref xPos);
depth--;
}
您遍历函数使用parentPosition
绘制一条线,并且还必须在结尾返回当前节点的x
和y
:
private Point traverseFunction(KeyValuePair<TKey, TValue> element, Point parentPosition, int depth, int xPos)
{
float x, y;
x = xPos * (horizontalSpacing + horizontalSize);
y = depth * (verticalSpacing + verticalSize);
RectangleF rect = new RectangleF(x, y, horizontalSize, verticalSize);
if(g.VisibleClipBounds.IntersectsWith(rect))
{
if(depth != treeHeight)
{
g.FillRectangle(Brushes.Black, x, y, horizontalSize, verticalSize);
g.DrawString(element.Key.ToString(), SystemFonts.DefaultFont, Brushes.Gold, x + 2, y + 2);
}
else
{
g.FillRectangle(Brushes.Red, x, y, horizontalSize, verticalSize);
g.DrawString(element.Key.ToString(), SystemFonts.DefaultFont, Brushes.White, x + 2, y + 2);
}
// Use the parent node's position to draw a line.
g.DrawLine(new Pen(Color.Black, 3), parentPosition, new Point(x, y));
}
Console.WriteLine(key + " : " + depth);
return new Point(x, y);
}