【问题标题】:How are large binary trees built?大型二叉树是如何构建的?
【发布时间】:2013-10-18 21:54:46
【问题描述】:

这是一个困扰我多年的问题。我想知道如何构建大型二叉树,我知道的唯一方法是创建一个将一个元素推送到树上的函数(一个名为 insert(); 的函数)。如果我有一个 3 元素树并且想要添加 5 个元素,我将不得不调用 insert 函数 5 次。这似乎是一个很糟糕的方法,如果我想添加 50 个元素怎么办?必须有比仅仅调用 insert() 函数五十次更好的方法。

【问题讨论】:

  • 在谷歌上快速搜索平衡二叉树,找到像wiki 这样的东西。

标签: tree


【解决方案1】:

如果数据是预先排序的,您可以递归地构建它。

基本上是为一些输入构建树:

  1. 创建一个新节点
  2. 如果输入只有一个条目,则节点的值就是那个条目
  3. 否则:
    1. 在节点的左子树中,放置从输入的前半部分构建的树
    2. 在节点的右子树中,放置从输入的后半部分构建的树

第三步将递归地应用到部分输入。

这是一些伪代码:

FUNCTION TREE (input -> node)
    IF input IS 1 ENTRY
        VALUE OF node IS entry OF input
    ELSE
        SPLIT input IN 2
        LEFT SUB-TREE OF node IS TREE(FIRST HALF OF input)
        RIGHT SUB-TREE OF node IS TREE(SECOND HALF OF input)

这里有一些LINQPad C# 代码可供您试验:

// Add the following two using-directives to LINQPad:
// System.Drawing
// System.Drawing.Imaging

static Bitmap _Dummy = new Bitmap(16, 16, PixelFormat.Format24bppRgb);
static Font _Font = new Font("Arial", 12);

void Main()
{
    var sorted = Enumerable.Range(1, 16).ToArray();
    var tree = BuildTree(sorted);
    Visualize(tree);
}

public Node<T> BuildTree<T>(T[] input)
{
    return BuildTree<T>(input, 0, input.Length);
}

public Node<T> BuildTree<T>(T[] input, int left, int right)
{
    if (right <= left)
        return null;

    if (right == left + 1)
        return new Node<T> { Value = input[left] };

    int middle = (left + right) / 2;
    return new Node<T>
    {
        Left = BuildTree<T>(input, left, middle),
        Right = BuildTree<T>(input, middle, right)
    };
}

public class Node<T>
{
    public T Value;
    public Node<T> Left;
    public Node<T> Right;

    public Bitmap ToBitmap()
    {
        Size valueSize;
        using (Graphics g = Graphics.FromImage(_Dummy))
        {
            var tempSize = g.MeasureString(Value.ToString(), _Font);
            valueSize = new Size((int)tempSize.Width + 4, (int)tempSize.Height + 4);
        }

        Bitmap bitmap;
        Color valueColor = Color.LightPink;
        if (Left == null && Right == null)
        {
            bitmap = new Bitmap(valueSize.Width, valueSize.Height);
            using (var g = Graphics.FromImage(bitmap))
                g.Clear(Color.White);
            valueColor = Color.LightGreen;
        }
        else
        {
            using (var leftBitmap = Left.ToBitmap())
            using (var rightBitmap = Right.ToBitmap())
            {
                int subNodeHeight = Math.Max(leftBitmap.Height, rightBitmap.Height);
                bitmap = new Bitmap(
                    leftBitmap.Width + rightBitmap.Width + valueSize.Width,
                    valueSize.Height + 32 + subNodeHeight);

                using (var g = Graphics.FromImage(bitmap))
                {
                    g.Clear(Color.White);
                    int baseY  = valueSize.Height + 32;

                    int leftTop = baseY; // + (subNodeHeight - leftBitmap.Height) / 2;
                    g.DrawImage(leftBitmap, 0, leftTop);

                    int rightTop = baseY; // + (subNodeHeight - rightBitmap.Height) / 2;
                    g.DrawImage(rightBitmap, bitmap.Width - rightBitmap.Width, rightTop);

                    g.DrawLine(Pens.Black, bitmap.Width / 2 - 4, valueSize.Height, leftBitmap.Width / 2, leftTop);
                    g.DrawLine(Pens.Black, bitmap.Width / 2 + 4, valueSize.Height, bitmap.Width - rightBitmap.Width / 2, rightTop);
                }
            }
        }

        using (var g = Graphics.FromImage(bitmap))
        {
            float x = (bitmap.Width - valueSize.Width) / 2;
            using (var b = new SolidBrush(valueColor))
                g.FillRectangle(b, x, 0, valueSize.Width - 1, valueSize.Height - 1);
            g.DrawRectangle(Pens.Black, x, 0, valueSize.Width - 1, valueSize.Height - 1);
            if (Left == null && Right == null)
                g.DrawString(Value.ToString(), _Font, Brushes.Black, x + 1, 2);
        }

        return bitmap;
    }
}

void Visualize<T>(Node<T> node)
{
    node.ToBitmap().Dump();
}

这是输出:

【讨论】:

  • 感谢您的回答。我给了你+1,但我想在奖励赏金之前完全相信如何做到这一点——我不是。我有点同意您提供的步骤,但您的伪代码与它不匹配。 '创建一个新节点',你如何递归地创建不同的节点,特别是使用像 C/C++ 这样的语言,你必须使用指针?在您的伪代码中,您也没有指定任何树的实际构建方式,这正是我正在寻找的。在您的步骤中,您说:“放置从前半部分构建的树”,但这是怎么发生的?
  • 另外 - 在您提供的 C# 代码中,特别是 Build(T[] input, int left, int right) 函数,'left' 和 'right' 的参数在哪里从?看起来它们仍然需要手动指定。如果是这样,那么我仍然必须手动而不是递归地构建树 - 这违背了整个目的。
  • "input" 是输入的排序数组。 left 从 0 开始,right 总是从数组的长度开始。
  • 我不明白您对 C/C++ 的评论,您当然会根据需要从堆中分配新的 Node 对象/结构。
【解决方案2】:

有。 Knuth 在我认为的 ACP 第三卷中给出了一种算法,用于从 排序 输入构建合理平衡的二进制文件。

【讨论】:

  • 猜猜现在我什至有更多的地方可以拿起那本书
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-06
  • 2010-10-24
  • 1970-01-01
  • 2011-08-25
相关资源
最近更新 更多