Stackoverflow исключение при прохождении BST

голоса
4

У меня реализовать ссылку на основе BST (бинарное дерево поиска) в C ++ для одного моего задания. Я написал весь свой класс, и все работает хорошо, но мое назначение спрашивает меня построить времена хода для:

a.  A sorted list of 50000, 75000, and 100000 items
b.  A random list of 50000, 75000, and 100000 items

Это нормально, я могу вставить цифры , но он также просит меня вызвать FindHeight()и CountLeaves()методы на дереве. Моя проблема заключается в том , что я реализовал две функции с использованием recursion. Так как у меня есть такой большой список номеров , я получаю получаю stackoverflowисключение.

Вот мое определение класса:

template <class TItem>
class BinarySearchTree
{
public:
    struct BinarySearchTreeNode
    {
    public:
        TItem Data;
        BinarySearchTreeNode* LeftChild;
        BinarySearchTreeNode* RightChild;
    };

    BinarySearchTreeNode* RootNode;

    BinarySearchTree();
    ~BinarySearchTree();

    void InsertItem(TItem);

    void PrintTree();
    void PrintTree(BinarySearchTreeNode*);

    void DeleteTree();
    void DeleteTree(BinarySearchTreeNode*&);

    int CountLeaves();
    int CountLeaves(BinarySearchTreeNode*);

    int FindHeight();
    int FindHeight(BinarySearchTreeNode*);

    int SingleParents();
    int SingleParents(BinarySearchTreeNode*);

    TItem FindMin();
    TItem FindMin(BinarySearchTreeNode*);

    TItem FindMax();
    TItem FindMax(BinarySearchTreeNode*);
};

FindHeight () Реализация

template <class TItem>
int BinarySearchTree<TItem>::FindHeight()
{
    return FindHeight(RootNode);
}

template <class TItem>
int BinarySearchTree<TItem>::FindHeight(BinarySearchTreeNode* Node)
{
    if(Node == NULL)
        return 0;

    return 1 + max(FindHeight(Node->LeftChild), FindHeight(Node->RightChild));
}

CountLeaves () реализация

template <class TItem>
int BinarySearchTree<TItem>::CountLeaves()
{
    return CountLeaves(RootNode);
}

template <class TItem>
int BinarySearchTree<TItem>::CountLeaves(BinarySearchTreeNode* Node)
{
    if(Node == NULL)
        return 0;
    else if(Node->LeftChild == NULL && Node->RightChild == NULL)
        return 1;
    else
        return CountLeaves(Node->LeftChild) + CountLeaves(Node->RightChild);
}

Я пытался думать о том, как можно реализовать эти два метода без рекурсии, но я полностью тупик. Кто-нибудь есть какие-нибудь идеи?

Задан 10/11/2011 в 00:52
источник пользователем
На других языках...                            


5 ответов

голоса
1

Для подсчета листьев без рекурсии, использовать понятие итератора вроде STL использует для RB-дерева , лежащего в основе std::setи std::map... Создание begin()и end()функции для вас дерева , которое идентифицирует заказанный первый и последний узел (в этом случае левых -Большая узел , а затем самый правый узел). Затем создайте функцию с именем

BinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)

что для данных current_node, возвращает указатель на следующий узел в дереве. Имейте в виду , для реализации этого на работу, вам потребуется дополнительный parentуказатель в вашем nodeтипе , чтобы помочь в процессе итерации.

Ваш алгоритм increment()будет выглядеть следующим образом :

  1. Проверьте, есть ли право-ребенок к текущему узлу.
  2. Если есть право-ребенок, использовать While-цикл, чтобы найти самый левый узел этого правого поддерева. Это будет «следующий» узел. В противном случае перейти к шагу 3.
  3. Если нет правого ребенка на текущем узле, а затем проверьте, чтобы увидеть, если текущий узел является левым потомком своего родительского узла.
  4. Если шаг # 3 верно, то «следующий» узел является родительским узлом, так что вы можете остановиться в этом пункте, в противном случае перейти к следующему шагу.
  5. Если шаг # 3 был ложным, то текущий узел правого ребенок родителя. Таким образом, вам нужно будет продолжать двигаться до следующего родительского узла, используя время цикла, пока не столкнутся с узлом, который является левым потомком своего родительского узла. Родитель этого левого дочернего узла тогда будет «следующий» узел, и вы можете остановиться.
  6. Наконец, если шаг # 5 возвращает вас в корень, то текущий узел является последним узлом в дереве, и итератор достиг конца дерева.

Наконец , вы будете нуждаться в bool leaf(const BinarySearchTreeNode* current_node)функцию , которая будет проверить данный узел является ли листовым узлом. Таким образом , вы счетчик функция может просто перебирать , хотя дерево и найти все узлы листьев, возвращая окончательный подсчет , как только это сделано.

Если вы хотите измерить максимальную глубину несбалансированного дерева без рекурсии, вы будете в вашем дереве insert()функции, необходимо следить за глубину , что узел был вставлен. Это может быть просто переменная в вашем nodeтипе , который устанавливается , когда узел вставляется в дерево. Вы можете перебирать три, и найти максимальную глубину листка-узел.

Кстати, сложность этого метода, к сожалению, будет O (N) ... далеко не так хорошо, как O (журнал N).

Ответил 10/11/2011 в 01:01
источник пользователем

голоса
3

Рекурсия на дереве с 100000 узлов не должно быть проблемой , если она сбалансирована. Глубина будет только возможно , 17, который не будет использовать очень много стеки в реализациях показаны на рисунке. (log2(100,000) = 16.61), Таким образом, кажется , что , может быть код , который строит дерево не балансировать его правильно.

Ответил 10/11/2011 в 01:02
источник пользователем

голоса
1

Может быть, вам нужно вычислить это, делая вставки. Хранить высоты узлов, то есть добавить целое поле, как высота в объекте Node. Кроме того, есть счетчики высоты и листья на дереве. При вставке узла, если его родитель (был) лист, изменение оленьей кожи количества листьев, но если нет, увеличить количество листьев на 1. Кроме того, высота нового узла высоты родителя + 1, следовательно, если это больше, чем текущая высота дерева, а затем обновить его. Ее домашнее задание, так что я не поможет с фактическим кодом

Ответил 10/11/2011 в 01:05
источник пользователем

голоса
2

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

Он имеет примеры, показывающие код, а также.

Ответил 10/11/2011 в 01:06
источник пользователем

голоса
1

Баланс вашего дерева время от времени. Если ваше дерево получает StackOverflow на FindHeight (), это означает , что ваше дерево способ несбалансированное. Если дерево сбалансировано она должна иметь только глубину около 20 узлов на 100000 элементов.

Самый простой (но довольно медленно) способ повторного уравновешивания неуравновешенного бинарного дерева выделить массив TItemдостаточно большой , чтобы держать все данные в дереве, вставить все данные в нем в отсортированном порядке, и удалить все узлы , Затем восстановить дерево из массива рекурсивно. Корень узел в середине. root->leftсередина левой половины, root->rightсередина правой половины. Повторите рекурсивно. Это самый простой способ , чтобы восстановить баланс, но это slowish и занимает много памяти временно. С другой стороны, у вас есть только сделать это , когда вы обнаружили , что дерево очень несбалансированное, (глубина на вставке более чем 100).

Другой (лучше) вариант балансировать во время вставки. Наиболее интуитивный способ сделать это, чтобы отслеживать, сколько узлов под текущим узлом. Если право ребенка более чем в два раза больше «дочерних» узлов как левый ребенок, «поворот» влево. И наоборот. Там же указание, изложенные о том, как сделать дерево вращается по всему интернету. Это делает вставки немного медленнее, но у вас нет иногда кабоба массивных киосков, что создает первый вариант. С другой стороны, вы должны постоянно обновлять все «дети» подсчетов, как вы делаете вращает, который не является тривиальным.

Ответил 10/11/2011 в 01:08
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more