绘制b树的算法

时间:2015-12-10 09:32:10

标签: c# algorithm system.drawing b-tree

我一直在寻找一种算法来将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

尽管实现了这个,我还是喜欢获得任何“更快”的算法,因为我不需要那么复杂的算法,也不会显示键,只显示节点。

1 个答案:

答案 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绘制一条线,并且还必须在结尾返回当前节点的xy

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);
}