简体   繁体   English

使用二叉树层序概念的垂直序遍历

[英]Vertical Order Traversal in using Level Order Concept of Binary Tree

I want to traverse a Binary Tree vertically.我想垂直遍历二叉树。 And I found a working code in Geeks for Geeks in C++.我在 Geeks for Geeks in C++ 中找到了一个工作代码。 I want to convert it into C# but I am unable to do so.我想将其转换为 C#,但我无法这样做。 Please guide me.请指导我。

Below is my attempt:以下是我的尝试:

// we always need the address of the Root Node come what may!
public class BstNode
{
    public int data      { get; set; }
    public BstNode left  { get; set; }
    public BstNode right { get; set; }

    public BstNode(int value) => data = value;
}

public class BstTree
{
    // For BST
    public BstNode Insert(BstNode root, int data)
    {
        if (root == null)
        {
            root       = new BstNode(data);
            root.left  = null;
            root.right = null;
        }
        else if (data > root.data)
             root.right = Insert(root.right, data);
        else root.left = Insert(root.left, data);

        return root;
    }
    // PROBLEM IN BELOW CODE
    public void VerticalOrderTraverse(BstNode root)
    {
        // Base case
        if (root == null)
            return;

        // Create a map and store vertical oder in
        Dictionary<int, List<int>> dict = new Dictionary<int, List<int>>();
        int hd = 0;

        // Create queue to do level order traversal.
        // Every item of queue contains node and
        // horizontal distance.
        Queue<Tuple<BstNode, int>> que = new Queue<Tuple<BstNode, int>>();
        que.Enqueue(new Tuple<BstNode, int>(root, hd));
        while (que.Count != 0)
        {
            // pop from queue front
            Tuple<BstNode, int> temp = que.Peek();
            que.Dequeue();
            hd = temp.Item2;
            BstNode node = temp.Item1;

            // insert this node's data in vector of hash
            dict.Add(hd, new List<int>(node.data)); // CONFUSED HERE

            if (node.left != null)
                que.Enqueue(new Tuple<BstNode, int>(node.left, hd - 1));
            if (node.right != null)
                que.Enqueue(new Tuple<BstNode, int>(node.right, hd + 1));
        }
        foreach (var item in dict)
            foreach (var ite in item.Value)
                Console.WriteLine(ite);
    }
}

class Program
{
    public static void Main()
    {
        BstNode root = null;
        BstTree bstTree = new BstTree();
        root = bstTree.Insert(root, 10);
        root = bstTree.Insert(root, 12);
        root = bstTree.Insert(root, 7);
        root = bstTree.Insert(root, 8);
        root = bstTree.Insert(root, 15);
        root = bstTree.Insert(root, 11);
        root = bstTree.Insert(root, 6);
        bstTree.VerticalOrderTraverse(root);
    }
}

Kindly note that I am getting exception in "VerticalOrderTraversal" Method.请注意,我在“VerticalOrderTraversal”方法中遇到异常。 This VerticalOrderTraversal is exact replica of Vertical Order Traversal in C++这个 VerticalOrderTraversal 是C++垂直顺序遍历的精确复制品

Exception: Key already exists in dictionary例外:键已存在于字典中

EDIT:编辑:

After adding this check still the Logic does not give correct output添加此检查后,逻辑仍然没有给出正确的输出

if (dict.ContainsKey(hd))
     dict[hd].Add(node.data);
else dict.Add(hd, new List<int>(node.data));

Where you have this:你有这个:

dict.Add(hd, new List<int>(node.data));

The original has this:原文是这样的:

m[hd].push_back(node->key);

Now when we look up in documentation what the operator std::map::operator[] does, we find that现在,当我们在文档中查找操作符std::map::operator[]作用时,我们发现

If k matches the key of an element in the container, the function returns a reference to its mapped value.如果 k 与容器中元素的键匹配,则该函数返回对其映射值的引用。

and importantly,并且重要的是,

If k does not match the key of any element in the container, the function inserts a new element with that key如果 k 与容器中任何元素的键都不匹配,则该函数会插入一个具有该键的新元素

The indexer of Dictionary<TKey, TValue>.Item has the same capability ("a set operation creates a new element with the specified key"), but whereas in C++, this implies construction of a new vector as the value of the new element, C# does not create an instance of List<int> for us, so a simple solution could be: Dictionary<TKey, TValue>.Item的索引器具有相同的功能(“集合操作创建具有指定键的新元素”),但在 C++ 中,这意味着构建一个新向量作为新元素的值,C# 不会为我们创建List<int>的实例,因此一个简单的解决方案可能是:

if (dict.ContainsKey(hd))
     dict[hd].Add(node.data);
else dict.Add(hd, new List<int>(node.data));
 /// <summary>
    /// We must calculate horizontal distance.
    /// Each node along with its hd shall be queued.
    /// Add hd and values in one hashset.
    /// </summary>
    /// <param name="root"></param>
    public void VerticalOrderTraversal(Node<T> root)
    {
        if (root == null)
            return;

        int hd = 0;
        Queue<Tuple<Node<T>,int>> q = new Queue<Tuple<Node<T>,int>>();
        Dictionary<int, HashSet<T>> ht = new Dictionary<int, HashSet<T>>();
        q.Enqueue(new Tuple<Node<T>, int>(root,hd));
        ht[hd] = new HashSet<T>();
        ht[hd].Add(root.Data);
        while (q.Count > 0)
        {
            Tuple<Node<T>, int> current = q.Peek();
            q.Dequeue();

            if (current.Item1.leftNode != null)
            {
                if (!ht.ContainsKey(current.Item2 -1))
                {
                    ht[current.Item2 - 1] = new HashSet<T>();
                    ht[current.Item2 - 1].Add(current.Item1.leftNode.Data);
                }
                else
                {
                    ht[current.Item2 - 1].Add(current.Item1.leftNode.Data);
                }
                q.Enqueue(new Tuple<Node<T>, int>(current.Item1.leftNode, current.Item2 - 1));


            }
            if (current.Item1.rightNode != null)
            {
                if (!ht.ContainsKey(current.Item2 + 1))
                {
                    ht[current.Item2 + 1] = new HashSet<T>();
                    ht[current.Item2 + 1].Add(current.Item1.rightNode.Data);
                }
                else
                {
                    ht[current.Item2 + 1].Add(current.Item1.rightNode.Data);
                }
                q.Enqueue(new Tuple<Node<T>, int>(current.Item1.rightNode, current.Item2 + 1));

            }
        }

        foreach (int key in ht.Keys)
        {
            foreach (T data in ht[key])
            {
                Console.WriteLine("Vertical Level " + key + " value " + data);
            }
        }
    }

there are few changes I made to the original code:我对原始代码做了一些更改:

  1. If the level (hd) already exists in the Dict, we need to add the node to the end of the list not to insert a new tuple of level and list.如果 Dict 中已经存在 level (hd),我们需要将节点添加到列表的末尾,而不是插入新的 l​​evel 和 list 元组。 so I added the if statement [if (dict.ContainsKey(hd))]所以我添加了 if 语句 [if (dict.ContainsKey(hd))]

  2. better use a sorted dictionary so that print will be from the least level not from the first level that was inserted.最好使用排序字典,以便打印将从最低级别而不是从插入的第一级别开始。

  3. In the original code, the list was created while inserting into the Dict, that was an issue because the node.data would be taken as the capacity of the list not as a data in the list.在原始代码中,列表是在插入 Dict 时创建的,这是一个问题,因为 node.data 将被视为列表的容量而不是列表中的数据。

     public void VerticalOrderTraverse(BstNode root) { // Base case if (root == null) return; // Create a map and store vertical oder in SortedDictionary<int, List<int>> dict = new SortedDictionary<int, List<int>>(); //used Sorted Dictionary int hd = 0; Queue<Tuple<BstNode, int>> que = new Queue<Tuple<BstNode, int>>(); que.Enqueue(new Tuple<BstNode, int>(root, hd)); while (que.Count != 0) { Tuple<BstNode, int> temp = que.Peek(); que.Dequeue(); hd = temp.Item2; BstNode node = temp.Item1; if (dict.ContainsKey(hd)) //No need to try creating a new list, add it to the existing dict[hd].Add(node.data); else { dict.Add(hd, new List<int>()); dict[hd].Add(node.data); } if (node.left != null) que.Enqueue(new Tuple<BstNode, int>(node.left, hd - 1)); if (node.right != null) que.Enqueue(new Tuple<BstNode, int>(node.right, hd + 1)); } foreach (var item in dict) foreach (var ite in item.Value) Console.WriteLine(ite); } } } }
public class Solution {
    
    public IList<IList<int>> result = new List<IList<int>>();
    public SortedDictionary<int, List<int>> IndexingItems = new SortedDictionary<int, List<int>>();
        
    public IList<IList<int>> VerticalTraversal(TreeNode root)
    {
        if(root == null) return result;
        Queue<(int, TreeNode)> queue = new Queue<(int, TreeNode)>();
        int hDis = 0;
        queue.Enqueue((hDis, root));
        
        while(queue.Count != 0)
        {
            (int, TreeNode) qTop = queue.Peek();
            queue.Dequeue();
            hDis = qTop.Item1;
            TreeNode current = qTop.Item2;
            AddItemToDictionary(hDis, current);
            
            if(current.left != null)
            {
                queue.Enqueue((hDis - 1, current.left));
            }
            
            if(current.right != null)
            {
                queue.Enqueue((hDis + 1, current.right));
            }
        }
        
        foreach(var item in IndexingItems)
        {
            var value = item.Value as List<int>;            
            result.Add(value);
        }
        return result;
    }
    
    public void AddItemToDictionary(int hDis, TreeNode node)
    {
        if(IndexingItems.ContainsKey(hDis))
        {
            IndexingItems[hDis].Add(node.val);
            // Console.WriteLine($"Updating {hDis} value");
        }
        else
        {
            // Console.WriteLine($"Adding new item {hDis} to Dictionary with {node.val} value");
            IndexingItems.Add(hDis, new List<int>(){ node.val });
        }
    }
}

I tried with this approach.我尝试过这种方法。 But I am not sure, why there is a few mismatch in the order of the data for same nodes.但我不确定,为什么相同节点的数据顺序有一些不匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM