Программа для создания дерева из обходов по порядку и по предзаказу (итеративный подход?) - PullRequest
0 голосов
/ 13 января 2019

Я пытался написать программу для создания дерева из заданных массивов символов inorder и preorder. Я могу сделать это с помощью рекурсивной программы легко. Но я не могу преобразовать это в итеративный подход. Пожалуйста, кто-нибудь, помогите мне с этим.

код.

    public BinaryTreeNode<char> ConstructTree(char[] preorder, char[] inorder, int start, int end)
            {
             Dictionary<char,int> InorderKeys = StoreInorderKeys(inorder);
                if(start>end)
                {
                    return null;
                }
                char key = preorder[preIndex];
                preIndex++;
                BinaryTreeNode<char> tr = new BinaryTreeNode<char>(key);
                if (start == end)
                {
                    return tr;
                }
                int rootNodeKey = InorderKeys[key] 
                tr.left = ConstructTree(preorder, inorder, start, (rootNodeKey - 1));
                tr.right = ConstructTree(preorder, inorder, rootNodeKey + 1, end);
                return tr;
            }  
     private Dictionary<char,int> StoreInorderKeys(char[] inorder)
            {
                Dictionary<char, int> d = new Dictionary<char, int>();
                for(int i=0;i<inorder.Length;i++)
                {
                    d[inorder[i]] = i;
                }
                return d;
            }

1 Ответ

0 голосов
/ 14 января 2019

Ваш комментарий, который

char[] inorder = (new List<char>() { 'F','B','A','E','H','C','D','I','G'}) 
char[] preorder = (new List<char>() { 'H', 'B', 'F', 'E', 'A', 'C', 'D', 'G', 'I' }) 

В частности, тот факт, что inorder не является символом, не означающим, что вы действительно используете несортированное "Двоичное дерево" вместо "Двоичное дерево поиска" , которое подразумевал мой первоначальный ответ.

Так как построить двоичное дерево, которое соответствует заданным inorder и preorder? Идея основана на свойствах preorder и inorder для двоичного дерева поиска, упомянутых в исходном ответе. В частности:

  • Если вы вставите значения в BST в порядке, указанном в preorder, вы получите дерево с этим предзаказом
  • Для BST-заказа обход узлов в порядке сортировки

Таким образом, простой способ создать такой BT - это построить его как BST, вставляя значения в порядке preorder и (здесь хитрый бит), используя inorder в качестве порядка сортировки. Другими словами, вы забываете о любой «естественной» сортировке и используете поддельную, которая точно отражает inorder.

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

class BinaryTreeNode<T>
{
    public readonly T value;
    public BinaryTreeNode<T> left = null;
    public BinaryTreeNode<T> right = null;

    public BinaryTreeNode(T value)
    {
        this.value = value;
    }

    public void InsertValue(T newValue, Comparer<T> comparer)
    {
        InsertValueNR(this, newValue, comparer);
    }

    private static void InsertValueNR(BinaryTreeNode<T> start, T newValue, Comparer<T> comparer)
    {
        for (BinaryTreeNode<T> cur = start; ;)
        {
            int cmp = comparer.Compare(cur.value, newValue);
            if (cmp == 0)
                throw new InvalidOperationException("value '" + newValue + "' is duplicated in the tree");
            else if (cmp < 0)
            {
                if (cur.left == null)
                {
                    cur.left = new BinaryTreeNode<T>(newValue);
                    return;
                }
                else
                    cur = cur.left;
            }
            else
            {
                if (cur.right == null)
                {
                    cur.right = new BinaryTreeNode<T>(newValue);
                    return;
                }
                else
                    cur = cur.right;
            }

        }

    }

    private void InsertValueRecursive(T newValue, Comparer<T> comparer)
    {
        int cmp = comparer.Compare(value, newValue);
        if (cmp == 0)
            throw new InvalidOperationException("value '" + newValue + "' is duplicated in the tree");
        else if (cmp < 0)
        {
            if (left != null)
                left.InsertValueRecursive(newValue, comparer);
            else
                left = new BinaryTreeNode<T>(newValue);
        }
        else
        {
            if (right != null)
                right.InsertValueRecursive(newValue, comparer);
            else
                right = new BinaryTreeNode<T>(newValue);
        }
    }



    class OrderComparer<T> : Comparer<T>
    {
        private readonly Dictionary<T, int> positions;

        public OrderComparer(List<T> order)
        {
            positions = new Dictionary<T, int>();
            for (int i = 0; i < order.Count; i++)
            {
                positions[order[i]] = i;
            }
        }

        public override int Compare(T x, T y)
        {
            return -Comparer<int>.Default.Compare(positions[x], positions[y]);
        }
    }

    public static BinaryTreeNode<T> ConstructTree(List<T> preorder, List<T> inorder)
    {
        var comparer = new OrderComparer<T>(inorder);
        var root = new BinaryTreeNode<T>(preorder[0]);
        for (int i = 1; i < preorder.Count; i++)
        {
            root.InsertValue(preorder[i], comparer);
        }

        return root;
    }

    public void PrintPreOrder()
    {
        PreOrder(ch => Console.Write(ch + " "));
        Console.WriteLine();
    }

    public void PrintInOrder()
    {
        InOrder(ch => Console.Write(ch + " "));
        Console.WriteLine();
    }

    public void PreOrder(Action<T> visitor)
    {
        visitor(value);
        if (left != null)
            left.PreOrder(visitor);
        if (right != null)
            right.PreOrder(visitor);
    }

    public void InOrder(Action<T> visitor)
    {
        if (left != null)
            left.InOrder(visitor);
        visitor(value);
        if (right != null)
            right.InOrder(visitor);
    }

}

Наиболее важной его частью является пользовательский OrderComparer, который используется вместо «естественного» порядка char. Также я оставил как нерекурсивный InsertValueNR, так и рекурсивный InsertValueRecursive, чтобы показать, как легко преобразовать последнее в первое.

Тестовый пример:

var inorder = new List<char>() { 'F', 'B', 'A', 'E', 'H', 'C', 'D', 'I', 'G' };
var preorder = new List<char>() { 'H', 'B', 'F', 'E', 'A', 'C', 'D', 'G', 'I' };
var root = BinaryTreeNode<char>.ConstructTree(preorder, inorder);
root.PrintInOrder();
root.PrintPreOrder();

Я получаю

F B A E H C D I G
H B F E A C D G I


Оригинальный ответ (для BST)

Примечание : В этом первоначальном ответе предполагалось, что термин "Двоичное дерево" здесь фактически означает "Двоичное дерево поиска" , то есть отсортированное.

Вы уверены, что хотите построить дерево по предварительному заказу и порядку одновременно? Если вы посмотрите на ответ в связанном вопросе, вы можете обнаружить, что один предварительный заказ полностью определяет дерево: вы просто делаете обычные вставки в дерево данных в указанном вами порядке. Тогда вы можете только проверить, соответствует ли он указанному порядку или нет. Вы не можете построить любое другое дерево с таким же предварительным заказом. Но на самом деле вы можете просто проверить, отсортирован ли порядок или нет: если он отсортирован - он совпадет. Если он не отсортирован - это недопустимый порядок для любого дерева.

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

На самом деле вы можете просто сделать произвольную случайную последовательность данных в массиве inorder и вставить их в этом порядке (как если бы это был предварительный порядок). Дело в том, что любое допустимое двоичное дерево с одинаковым содержимым (т. Е. Одинаковым набором значений во всех узлах, но, возможно, с различной внутренней структурой) будет иметь один и тот же порядок следования только по определению этого порядка.

...