【问题标题】:Keeping reference to a nested List<T> in C#在 C# 中保持对嵌套 List<T> 的引用
【发布时间】:2021-07-13 07:30:16
【问题描述】:

我的任务是在树中找到路径,其中节点的总和构成一个特定的总和,在开头给出。

例子:

在我的实现中,我将结果保存在嵌套列表中,如下所示:

private void DfsPathGivenSum(Tree<T> node, List<T> list, int sum, List<List<T>> results)
{
    list.Add(node.Key);

    foreach (var child in node.children)
    {
        this.DfsPathGivenSum(child, list, sum, results);
    }

    var currentPathSum = list.Sum(x => int.Parse(x.ToString()));
    if (currentPathSum == sum)
    {
        results.Add(list);
    }

    list.RemoveAt(list.Count - 1);
}

当我从另一个方法调用这个方法时:

public List<List<T>> PathsWithGivenSum(int sum)
{
    var list = new List<T>();
    var results = new List<List<T>>();
    this.DfsPathGivenSum(this, list, sum, results);

    return results;
}

对内部列表的引用似乎丢失了。我该如何处理这个问题?

【问题讨论】:

  • 你打电话给list.RemoveAt(..)为什么?
  • 'list' 跟踪当前路径,因此 list.RemoveAt() 删除不再位于当前路径上的元素。
  • 这里的教训是递归。通过递归,您可以“走”树。这是一些documentationhere
  • 您不应该从原始列表中删除项目。
  • 那我怎么能跟随路径元素呢?也许我没有问清楚这个问题,但是实现很完美,当我调用递归方法时,嵌套的 List 丢失了元素。

标签: c# data-structures reference tree


【解决方案1】:

改变

results.Add(list);

results.Add(list.ToList());

即您想在将当前列表添加到结果时创建当前列表的副本,否则您将只有一堆对同一个列表的引用,最后将是空的。

我还建议使用比ToStringint.Parse 更好的方法将节点转换为数字。例如,通过采用可提供给 .sum 方法的 Func&lt;T, int&gt; 参数。

您还可以通过保持运行总和来稍微改进算法,以避免需要为每个访问的节点循环遍历所有先前的节点。

【讨论】:

  • 这是一个很好的答案,谢谢。但是,我想问一下 Func 如何改进代码,因为它仍在将泛型转换为字符串,然后将其解析为 int: Func selector = str => int.Parse(str.ToString( ));
  • @georgi popov 因为调用者应该知道T 的实际类型,所以它应该能够使用对象的实际属性(或值本身,如果 T 是 int),而不是依赖于具有重写 ToString 方法的类,该方法返回可解析的字符串。
  • 编译器如何接受这样的事情?
  • @georgi Popov 编译器接受委托,因为它被写入这样做?假设我有一个带有 Score 属性的 Node 类,您可以在调用该方法时提供像 myNode =&gt; myNode.Score 这样的委托参数,类型推断会将其映射到 Func 委托类型。跨度>
【解决方案2】:

正如@JonasH 所说,您肯定需要从复制的节点集合中工作。

// Pseduocode:
// For each node
//   Find Path and Sum
//   If Sum correct
//     Add Path to Result


private class Node
{
    public Node Parent;
    public int score;
}

private void DfsPathGivenSum(List<Node> allNodes, int targetSum, List<List<Node>> results) {

    results.Clear();

    // Copy list of Nodes into Queue
    var pending = new Queue<Node>(allNodes); 

    // For each node in queue
    while (pending.Count > 0) {
        var currentNode = pending.Dequeue();

        // Create 'new' Path trace starting at currentNode
        var path = new List<Node> { currentNode };
        var sum = currentNode.score;

        // Iterate until at root node
        while (currentNode.Parent != null) { 
            currentNode = currentNode.Parent; // step to parent node
            path.Add(currentNode); // add to trace
            sum += currentNode.score; // add to sum
        }

        if (sum == targetSum)
            results.Add(path);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多