从有序遍历和预遍历遍历创建树的程序(迭代方法?)

时间:2019-01-13 17:56:02

标签: c# algorithm recursion iteration binary-tree

我一直试图编写一个程序,以从给定的有序和预排序char数组创建树。我可以通过递归程序轻松地做到这一点。但是我无法以迭代方式进行转换。请有人帮我这个忙。

代码。

    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 :(得分:0)

您的评论

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不被扭曲的事实表明,您实际上使用的是未排序的"Binary Tree",而不是我的原始答案所暗示的"Binary Search tree"

那么如何构建与给定的inorderpreorder匹配的二叉树?这个想法基于原始答案中提到的二分搜索树的preorderinorder的属性。特别是:

  • 如果您按preorder中的顺序将值插入BST中,则会得到带有该预排序的树
  • 对于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)

注意:此初始答案假设此处的术语"Binary Tree"实际上是"Binary Search tree",即经过排序的术语。

您确定要同时按顺序构建树吗?如果查看链接的问题中的answer,您可能会发现仅预订单就完全指定了树:您只需按照给定的顺序对数据树进行常规插入即可。然后,您只能检查它是否与给定的顺序匹配。您无法以相同的预定顺序构建其他任何树。但是实际上您只需要检查订单是否已排序即可:如果已排序-它将匹配。如果未排序-则不是任何树的有效顺序。

对于有序,有一种非常简单的方法来构建效率很低但在技术上仍然有效的二叉树:将第一个值放入根,然后将每个下一个值添加为其正确的子级。这棵树显然符合顺序。这实际上与使用顺序作为其顺序来构建树相同。

实际上,您可以对有序数组中的数据进行任意随机混洗,然后按该顺序插入它们(就像它是预购商品一样)。事实是,仅凭顺序的定义,任何具有相同内容(即,所有节点中都具有相同的一组值,但内部结构可能不同)的有效二叉树将具有相同的顺序。