Вывести узлы двух двоичных деревьев в порядке возрастания - PullRequest
5 голосов
/ 11 сентября 2011

Учитывая два бинарных дерева поиска, выведите узлы в порядке возрастания с временной сложностью O (n) и пространственной сложностью: O (1)

Деревья не могут быть изменены. Разрешен только обход.

Проблема, с которой я сталкиваюсь, связана с решением пространства O (1). Если бы не было этого ограничения, его можно было бы легко решить.

Ответы [ 3 ]

2 голосов
/ 12 сентября 2011

Единственный способ сделать это в пространстве O (1) - это если узлы знают своего родителя. В противном случае вы не сможете даже пройти по дереву, если не будет дополнительной помощи. Однако с этим ограничением это снова легко и возвращается к обходу дерева, но без рекурсии. Сложная часть, вероятно, заключается в том, чтобы знать, с какого дерева вы пришли, когда переходите от узла к его родительскому элементу (p), и не может хранить эту информацию, поскольку для этого потребуется пространство O (log N). Тем не менее, вы знаете последнее значение, которое вы вывели. Если он меньше, чем у p, идите направо, в противном случае перейдите к родителю p.

0 голосов
/ 12 сентября 2011

У меня есть решение этой проблемы.Я написал свое решение на C #, потому что это мой самый сильный язык, но я надеюсь, что вы поймете основную идею.Предположим, что у каждого узла дерева есть 3 ссылки: на левый, правый и родительский узлы.

Итак, у нас есть BinaryTree.Как мы могли бы напечатать это?Очевидно:

this._tree.Print();

Это было не очень сложно.Но как мы могли бы построить метод Print, если нам следует избегать рекурсии (поскольку последний использует память O (log (n)))?Вы когда-нибудь читали о ленивых списках (или потоках)?Ленивый список не хранит весь список в памяти, но знает , как рассчитать следующий элемент на основе текущего элемента .В каждый момент ленивый список выделяет О (1) памяти.Итак, предположим, нам удалось описать ленивый список для дерева.Тогда метод Print очень прост:

public static void Print<T>(this BinaryTree<T> tree)
    where T : IComparable<T>
{
    var node = new TreeNodeWalker<T>(tree.Root, WalkerState.FromParent);
    while (node != null)
    {
        node = node.WalkNext();
    }
}

Во время этого фрагмента кода вы можете обнаружить одну незнакомую сущность: TreeNodeWalker.Этот объект содержит узел дерева , который должен быть пройден, состояние , который сигнализирует, в какой момент обхода был создан этот ходок, и метод , который дает следующий ходок.Короче говоря, Walker выполняет следующие действия:

  • Если мы добавляем любое поддерево из родительского узла, мы должны пройти левое поддерево.
  • Если мы вышли из левого поддерева, мы должны вывести значение узла и пройти правое поддерево.
  • Если мы вышли из правого поддерева, мы должны пройти родительский.

Это может быть представлено в коде следующим образом:

public class TreeNodeWalker<T>
    where T:IComparable<T>
{
    // Tree node, for which walker is created. 
    private readonly BinaryTreeNode<T> _node;

    // State of walker.
    private readonly WalkerState _state;

    public TreeNodeWalker(BinaryTreeNode<T> node, WalkerState state)
    {
        this._node = node;
        this._state = state;
    }

    public TreeNodeWalker<T> WalkNext()
    {
        if (this._state == WalkerState.FromParent)
        {
            // If we come to this node from parent 
            // we should walk left subtree first.
            if (this._node.Left != null)
            {
                return new TreeNodeWalker<T>(this._node.Left, WalkerState.FromParent);
            }
            else
            {
                // If left subtree doesn't exist - return this node but with changed state (as if we have already walked left subtree).
                return new TreeNodeWalker<T>(this._node, WalkerState.FromLeftSubTree);
            }
        }
        else if (this._state == WalkerState.FromLeftSubTree)
        {
            // If we have returned from left subtree - current node is smallest in the tree
            // so we should print it.
            Console.WriteLine(this._node.Data.ToString());

            // And walk right subtree...
            if (this._node.Right != null)
            {
                //... if it exists
                return new TreeNodeWalker<T>(this._node.Right, WalkerState.FromParent);
            }
            else
            {
                // ... or return current node as if we have returned from right subtree.
                return new TreeNodeWalker<T>(this._node, WalkerState.FromRightSubTree);
            }
        }
        else if (this._state == WalkerState.FromRightSubTree)
        {
            // If we have returned from right subtree, then we should move up.
            if (this._node.Parent != null)
            {
                // If parent exists - we compare current node with left parent's node 
                // in order to say parent's walker which state is correct. 
                return new TreeNodeWalker<T>(this._node.Parent, this._node.Parent.Left == this._node ? WalkerState.FromLeftSubTree : WalkerState.FromRightSubTree);
            }
            else
            {
                // If there is no parent... Hooray, we have achieved root, which means end of walk. 
                return null;
            }
        }
        else
        {
            return null;
        }
    }
}

Вы можете увидеть много места в коде и принять решение, что O (1) требуется памятьне выполнено.Но после получения следующего элемента ходунка нам больше не нужен предыдущий.Если вы пишете код на C ++, не забудьте освободить память.В качестве альтернативы, вы могли бы вообще избежать выделения экземпляра нового Walker, изменив внутренние переменные state и node (вы всегда должны возвращать эту ссылку в соответствующих местах).

Что касается сложности времени - это O (n).На самом деле O (3 * n), потому что мы посещаем каждый узел максимум три раза.

Удачи.

0 голосов
/ 12 сентября 2011

, если мы говорим о BST, как определено википедией:

The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.

с дополнительным перком, что каждый узел знает своего родителя, тогда следующий код C делает свое дело (надеюсь, вам нравится C, я приложил немало усилий в этих 300 строках демонстрационного приложения: D)

http://pastebin.com/MiTGqakq

(обратите внимание, что я не использовал рекурсию, потому что рекурсия технически никогда не является пробелом O (1). Причина этого в том, что каждый вызов функции использует копии переданных параметров, таким образом выделяя дополнительное пространство, делая O_space зависимым от числа вызовов -> не в O (1) пробел.)

РЕДАКТИРОВАТЬ: хорошо, исправлена ​​версия связана. веселиться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...