C # AVL-Tree: метод замедляет мою программу, но почему?

My ConsoleApplication создает правильное AVL-дерево из ввода. Для моего университета мне нужно сделать программу, которая:

  • правильно вставить данные в AVL-Tree
  • держать его сбалансированным
  • дать вывод достаточно быстро и правильно для определенного ввода

Мой вопрос: почему метод/часть моей программы, которую я представлю позже в этом разделе, настолько медленная (96% всего времени выполнения программы)?

Другие методы/части в моей программе, которые также используют обходы по дереву, занимают около 0,05% или меньше моей программы.


Я объясню часть/метод ранг узла в дереве, тот, который, в соответствии с DotTrace (инструментом аналитики), этот метод занимает 96% всей моей программы (все остальные методы занимают около 0,05% или меньше). И именно поэтому я получаю ограничение по времени на свое задание, когда оно представлено в системе думджейдж.


  • если строка ввода начинается с T: вставить новый узел в дерево AVL

  • если входная строка начинается с G: определяемый ранг указанного ранга узла - это количество людей, которые получили более высокий балл, чем вы +1, чем вывод с помощью Console.Writeline(variable);

    пример:

  • значения узлов: 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, которое мы сравниваем с другими узлами, чтобы определить, является ли оно выше (счетчик++) или ниже (ничего не делать).

Когда нет более высоких узлов, чем 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);
        }
    }

person BARJ    schedule 03.08.2012    source источник
comment
Сколько вызовов вашей функции Rank (относительно ваших функций вставки и извлечения)?   -  person Iain    schedule 03.08.2012
comment
для каждой InputLine, которая начинается с G +(node.ID), возвращает 1 outputline, содержащую rank(int) нужного узла. Итак: функция Rank будет вызываться только один раз для каждой InputLine, начинающейся с G.   -  person BARJ    schedule 03.08.2012
comment
В своем вопросе вы сказали, что функция Rank занимает 95% времени выполнения. Это можно объяснить медленной работой функции Rank, но также можно объяснить тем, что функция Rank вызывается в двадцать раз чаще, чем что-либо еще. В последнем случае функция Rank не будет особенно замедлять вашу программу (хотя ее все же стоит оптимизировать).   -  person Iain    schedule 03.08.2012
comment
Функция ранга вызывается 50 000 раз, как и другие функции, которые выдают результат. Функции распределены поровну. Таким образом, функция Rank не работает должным образом. Спасибо за вашу помощь и время.   -  person BARJ    schedule 05.08.2012


Ответы (1)


Есть три вещи, на которые стоит обратить внимание.

Во-первых, у вас есть некоторые условные выражения, которые я считаю ненужными. В UnknownTreeWalk мы проверяем, меньше ли значение узла, чем compareValue. Однако compareValue — это значение начального узла, и когда мы вызываем UnknownTreeWalk, мы всегда находимся справа от этого начального узла. Нахождение справа означает, что его значение больше, поэтому проверка не нужна. Там могут быть некоторые такие же крошечные изменения, которые вы можете сделать, чтобы сделать вещи немного быстрее.

Во-вторых, у вас может быть много промахов кеша ЦП. Вы можете попытаться организовать непрерывное расположение ваших TreeNodes в памяти. В вашем случае это, наверное, не критично.

В-третьих, и это самое главное, я подозреваю, что вы тратите много времени на то, чтобы бегать вокруг деревьев, пытаясь понять, какого они размера. Вы можете сохранить размер каждого поддерева в его объекте MyNode, а затем просто проконсультироваться с ним вместо того, чтобы подсчитывать все места. Именно это, я думаю, скорее всего, поможет вам быстро добраться туда, куда вам нужно.

Наконец, возможна гораздо более простая реализация Rank. Я бы посоветовал вам взять то, что вы узнали о проблеме из этой реализации, и написать новую, думая об этих знаниях и о подсчете всех узлов справа от этой.

person Iain    schedule 03.08.2012
comment
Я сравниваю узлы в правом поддереве с начальным узлом, потому что все еще есть вероятность, что есть узлы, которые имеют то же значение, что и начальный узел, и я хочу увеличивать счетчик только тогда, когда есть узел, который выше, чем текущий узел. Ваш третий вариант кажется хорошим и быстрым решением для решения моей проблемы. При вставке новых узлов мне нужно только записать, сколько дочерних элементов имеет каждый узел, и когда есть узлы, равные начальному узлу, я должен вставить их как левый дочерний элемент. но разве это не сильно замедляет мою функцию вставки? Спасибо за вашу помощь и время. - person BARJ; 05.08.2012