【问题标题】:Recursive tree traversal - saving the path taken to each node递归树遍历 - 保存到每个节点的路径
【发布时间】:2018-09-26 04:46:28
【问题描述】:

我正在尝试创建一个遍历数字字典的递归函数,然后输出每个节点的遍历路径。

数据结构如下所示:

var tree = new Dictionary<int, List<int>>()
{
    {888, new List<int>() {44}},
    { 44, new List<int>() {183, 275, 100, 216}},
    {100, new List<int>() {299, 400}},
    {299, new List<int>() {504}},
    {216, new List<int>() {33}}
};

所以表示数据结构的树形结构看起来像这样

                 888
                /   \
               44    (many other nodes and subnodes)
           / /  \  \
        183 275 100 216
               /  \    \
              299 400   33
             /
            504

我想返回一个输出类似这样的列表的列表

[888, 44, 183]
[888, 44, 275]
[888, 44, 100, 299, 504]
[888, 44, 100, 400]
[888, 44, 216, 33]

这是迄今为止的内容,可能不正确。我可以成功地得到一些我想要的结果。我认为问题在于它没有删除具有访问过所有子节点的子节点的节点。

public List<int[]> FindPaths(int currentNode, Dictionary<int, List<int>> tree, List<int> temp, List<int> visitedNodes)
    {
            if (tree.ContainsKey(currentNode))
            {
                if (!visitedNodes.Contains(currentNode))
                {
                    visitedNodes.Add(currentNode);
                }

                foreach (var node in tree[currentNode])
                {
                    visitedNodes.Add(node);
                    temp.Add(node);
                    // call method again with new values
                    FindPaths(node, tree, temp, visitedNodes);                                            
                }

                // if node has no children left and is not a leaf node
                // do something here?
            }
            else // we have reached a leaf node
            {
                paths.Add(temp.ToArray());
                temp.RemoveAt(temp.Count - 1);
                return paths;
            }
            return paths;
    }

调用函数

paths = new List<int[]>();
var temp = new List<int>();
var vistedNodes = new List<int>();
var result = FindPaths(888, tree, temp, vistedNodes);

谁能帮我得到我想要的输出?如果可能的话,我想让它递归地工作

【问题讨论】:

  • 您的字典定义了一个有向图,其中整数是顶点。 您的图表是否保证是非循环的
  • 如果不是,那么考虑1 -&gt; {2, 3}, 2 -&gt; { 3 }, 3 -&gt; { 2 },我们是从1开始遍历的。是你想要的解决方案{ [1, 2, 3], [1, 3, 2] }吗?还是因为3已经在第一个遍历中而拒绝第二次遍历?
  • 另外,一个建议:使用不可变数据类型。如果您停止使用可变集合,则可以更有效、更轻松地解决此类问题。

标签: c# dictionary recursion


【解决方案1】:

试试这个:

public List<List<int>> FindPaths(int currentNode, Dictionary<int,List<int>> tree)
{
    List<List<int>> paths = new List<List<int>>();

    if(tree.ContainsKey(currentNode))
    {
        foreach(var node in tree[currentNode])
        {
            var subPaths = FindPaths(node,tree);
            foreach(var subPath in subPaths)
            {
                subPath.Insert(0,currentNode);
                paths.Add(subPath);
            }
        }
    }
    else
    {
        paths.Add(new List<int>(){currentNode});
    }
    return paths;
}

请注意,这是假设您没有循环路径 (A->B->C->A)。如果字典包含一个,您将陷入困境。如果这是可能的,您必须跟踪访问过的节点并避免重新处理它们。

【讨论】:

    【解决方案2】:

    与您的方法类似,此解决方案是在访问时继续添加节点,一旦该节点不存在键就收集列表,并在其迭代结束时删除元素。

    void findPaths(int root, Dictionary<int,List<int>> tree,List<List<int>> pathList, List<int> visitedNodes)
    {
        visitedNodes.Add(root);
        if(tree.ContainsKey(root))
        {
            foreach(int v in tree[root])
            {
                findPaths(v,tree,pathList,visitedNodes);
                visitedNodes.RemoveAt(visitedNodes.Count - 1);
            }
        }
        else
        {
                pathList.Add(new List<int>(visitedNodes));
        }
    }
    

    【讨论】:

      【解决方案3】:

      使用这种方法很容易解决

      //Just some helper method
      public static IEnumerable<TKey[]> GetPaths<TKey, TDictImpl>(this IDictionary<TKey, TDictImpl> dict) where TDictImpl : IEnumerable<TKey>
      {
          var watchlist = new List<TKey>();
          var outlist = new List<List<TKey>>();
          GetPaths(dict, dict.Keys.First(), watchlist, outlist, new List<TKey> { dict.Keys.First() });
          return outlist.Select((l) => l.ToArray());
      }
      private static void GetPaths<TKey, TDictImpl>(this IDictionary<TKey, TDictImpl> dict, TKey parent, List<TKey> watchlist, List<List<TKey>> outlist, List<TKey> current) where TDictImpl : IEnumerable<TKey>
      {
          //Try to get our child from the dict
          if (!dict.TryGetValue(parent, out TDictImpl subs))
          {
              //No child found, no further navigation needs to be done
              return;
          }
          foreach (var it in subs)
          {
              //Simple check to make sure we do not end in some endless loop
              if (watchlist.Contains(it))
              {
                  throw new FormatException($"The key {it.ToString()} was attempted to be traversed a second time in {parent.ToString()}.");
              }
              else
              {
                  watchlist.Add(it);
              }
              //Add everything to our outlist
              var tmp = current.Concat(new TKey[] { it }).ToList();
              outlist.Add(tmp);
              //Proceed with possible childnodes
              GetPaths(dict, it, watchlist, outlist, tmp);
          }
      }
      

      生产
      var tree = new Dictionary<int, List<int>>()
      {
          {888, new List<int>() {44}},
          { 44, new List<int>() {183, 275, 100, 216}},
          {100, new List<int>() {299, 400}},
          {299, new List<int>() {504}},
          {216, new List<int>() {33}}
      };
      var res = Random.GetPaths(tree);
      

      跟随输出

      [888, 44]
      [888, 44, 183]
      [888, 44, 275]
      [888, 44, 100]
      [888, 44, 100, 299]
      [888, 44, 100, 299, 504]
      [888, 44, 100, 400]
      [888, 44, 216]
      [888, 44, 216, 33]
      

      【讨论】:

        猜你喜欢
        • 2012-12-12
        • 1970-01-01
        • 2018-04-04
        • 1970-01-01
        • 2023-03-14
        • 2018-07-12
        • 2014-03-11
        • 1970-01-01
        • 2012-03-21
        相关资源
        最近更新 更多