【问题标题】:C# AVL-Tree: Method slowing down my program but why?C# AVL-Tree:方法减慢了我的程序,但为什么?
【发布时间】:2012-08-03 07:07:41
【问题描述】:

我的 ConsoleApplication 根据输入生成正确的 AVL-Tree。 对于我的大学,我需要制定一个程序:

  • 在 AVL 树中正确插入数据
  • 保持平衡
  • 提供足够快的输出并正确地处理某些输入

我的问题是为什么我在本主题后面介绍的程序的方法/部分如此缓慢(96% 的总时间程序运行)?

我的程序中也使用 treewalks 的其他方法/部分大约占我程序的 0.05% 或更少


我将解释部分/方法“树中节点的等级”,根据 DotTrace(分析工具),此方法占我整个程序的 96%(所有其他方法大约需要 0.05 % 或更少)。这就是为什么我在使用 dumjudge 系统提交作业时会获得时间限制。


  • 如果输入行以 T 开头: 在 AVL 树中插入新节点

  • 如果输入行以 G 开头: 确定指定节点的秩 rank 是比使用 Console.Writeline(variable) 获得的分数高 +1 的人数;

    示例:

  • 节点值:x(10) y(5) z(2) k(5) l(4) m(9)

  • 节点等级:X(1) y(3) z(6) k(3) l(5) m(2)


  • 如果输入行以其他内容开头: 无需解释这部分工作正常

我已经尝试了很多东西,但我不明白为什么它会变慢我希望你们能帮助我,让我看看我做错了什么。


变量:

  • MyAVLTree T:包含 AVL 树的根
  • MyNode NodeX,包含我们要从中确定排名的节点
  • Int compareVALue,是 nodeX 的值,我们将它与其他节点进行比较以确定它是更高(counter++)还是更低(什么都不做)

当没有比 nodeX 更高的节点时,该方法将返回包含节点等级的计数器变量,以便将其打印为输出。


在 AVL 树中产生输出或插入数据的所有输入行大约占我程序的 0.05% 或更少...除了我的程序中产生/返回 AVL 树中节点等级的方法/部分(96 %)

我希望我的代码是可读的,在此先感谢您的帮助和时间。


 public static int RankElement(MyAVLTree T, MyNode nodeX, int compareValue)
    {
        int counter = 1;

        while (true)
        {
            if (nodeX == T.root)
            {
                UnkownTreeWalk(T, nodeX.Right, compareValue, ref counter);
                return counter;
            }
            else if (nodeX == nodeX.Parent.Right)
            {
                UnkownTreeWalk(T, nodeX.Right, compareValue, ref counter);

                while (nodeX == nodeX.Parent.Right)
                {
                    nodeX = nodeX.Parent;
                    if (nodeX == T.root)
                    {
                        return counter;
                    }
                }
                nodeX = nodeX.Parent;
                if (nodeX.playerScore > compareValue)
                    counter++;
            }
            else
            {
                UnkownTreeWalk(T, nodeX.Right, compareValue, ref counter);

                nodeX = nodeX.Parent;
                if (nodeX.playerScore > compareValue)
                    counter++;
            }
        }


    }

    public static void UnkownTreeWalk(MyAVLTree T, MyNode nodeX, int compareValue, ref int counter)
    {
        if (nodeX != null)
        {
            if (nodeX.playerScore > compareValue)
            {
                counter++;
            }
            UnkownTreeWalk(T, nodeX.Right, compareValue, ref counter);

            UnkownTreeWalk(T, nodeX.Left, compareValue, ref counter);
        }
    }

【问题讨论】:

  • Rank 函数有多少次调用(相对于插入和检索函数)?
  • 对于每个以 G +(node.ID) 开头的 InputLine 返回 1 个输出行,其中包含所需节点的 rank(int)。所以:对于每个以 G 开头的 InputLine,Rank Function 只会被调用一次
  • 您在问题中说 Rank 函数占用了 95% 的运行时间。这可以用 Rank 函数慢来解释,但也可以用 Rank 函数被调用的次数比其他任何方法多 20 倍来解释。在后一种情况下,Rank 函数不会特别减慢您的程序(尽管它仍然值得优化)。
  • 排名函数被调用了 50.000 次,其他给出输出的函数也是如此。功能是同等划分的。所以 Rank 功能不能正常工作。感谢您的帮助和时间。

标签: c# performance avl-tree


【解决方案1】:

需要研究三件事。

首先,您有一些我认为不必要的条件。在 UnknownTreeWalk 中,我们检查节点的值是否小于 compareValue。然而,compareValue 是初始节点的值,当我们调用 UnknownTreeWalk 时,我们总是在该初始节点的右侧。向右意味着它的值更大,所以检查是不必要的。您可以进行一些类似的微小更改以使事情变得更快捷。

其次,您可能有很多 CPU 缓存未命中。您可以尝试安排您的 TreeNode 在内存中连续排列。对您而言,这可能没什么大不了的。

第三,也是最重要的一点,我怀疑你会花很多时间在树木周围跑来跑去,研究它们的大小。您可以将每个子树的大小保留在它的 MyNode 对象中,然后只需查阅它,而不是到处计算。我认为这是最有可能让您快速到达需要的地方。

最后,Rank 的实现可能要简单得多。我鼓励你利用从这个实现中学到的关于这个问题的知识,然后写一个新的,考虑这些知识并计算这个节点右侧的所有节点。

【讨论】:

  • 我将右子树中的节点与初始节点进行比较,因为仍有可能存在与初始节点具有相同值的节点,我只想在有比当前节点高的节点。您的第三个选项似乎是解决我的问题的一个很好且快速的解决方案。在插入新节点时,我只需要记录每个节点有多少子节点,当有节点等于初始节点时,我应该将它们作为左子节点插入。但是,这不会减慢我的插入功能吗?感谢您的帮助和时间。
猜你喜欢
  • 2012-01-15
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-04
相关资源
最近更新 更多