【问题标题】:Create flat list from tree ordered by the tree order从按树顺序排序的树创建平面列表
【发布时间】:2015-02-01 04:00:31
【问题描述】:

我正在研究 C#,我正在寻找一种简单的方法来获取表示树的列表并将树扁平化为与树中顺序相同的列表。

每个节点有 4 个属性:
* 身份证
* 家长 ID,
* 序列
* 名称

public class MyNode  
{  
   public int ID{get;set;}  
   public int ParentID{get;set;}  
   public int SeqID{get;set;}  
   public string Name{get;set;}  
}  

我有一个 MyNode 的集合:

List<MyNode> FlatListOfNodes{get;set;}

我正在寻找如下所示的内容:
FlatListOfNodes.OrderBy(something).DoAnotherThing1(somthing)...

这将按照与树中相同的顺序对列表进行排序。

例如,如果会有这棵树:

-- Parent1
     -- Child 1.1  
     -- Child 1.2  
        -- grandson 1.2.1  
        -- grandson 1.2.2  
        -- grandson 1.2.3
    -- Child 1.3
-- Parent2
     -- Child 2.1  
     -- Child 2.2  
        -- grandson 2.2.1  
        -- grandson 2.2.2  
        -- grandson 2.2.3
    -- Child 2.3
        -- grandson 2.3.1
        -- grandson 2.3.2

我想以相同的顺序在一个平面列表中表示它。

在这个例子中:

  • Parent1 的 ID=32,ParentID=-1,SeqID=1,Name="Parent1"
  • Child1.2 的 ID=412,ParentID=32,SeqID=2,Name="Child1.2"
  • 孙子 1.2.1 的 ID=231,ParentID=412,SeqID=1,Name="grandson 1.2.1"
  • Parent2 的 ID=345,ParentID=-1,SeqID=2,Name="Parent2"
  • 具有 ID=785、ParentID=345、SeqID=3、Name="Child 2.3" 的 Child2.3
  • 孙子 2.3.1 的 ID=854,ParentID=785,SeqID=1,Name=“孙子 2.3.1”

最好的方法是什么?
有没有办法用 linq 做到这一点?

谢谢!

【问题讨论】:

  • 可以逆向遍历列表
  • 我想你应该看看Enumerable.SelectMany
  • SelectMany 在这种情况下不能使用。它不是集合中的集合
  • 我认为您正在寻找预购遍历,网上有很多参考资料。 (并且可以使用SelectMany,但它比更直接的递归迭代器更难:这在没有 LINQ 的情况下更容易。)
  • 是否可以在 MyNode 中添加另一个字符串属性,可以设置为整体序列(1.2、2.3.1 等)。这可以通过将当前节点的序列连接到父节点的序列来完成。按此属性排序应给出您要查找的顺序。

标签: c# algorithm linq tree


【解决方案1】:

您可以分两次执行此操作。首先,您创建一个节点字典,每个节点都有一个子节点列表。

class TreeNode
{
    public MyNode NodeData;
    public List<MyNode> Children = new List<MyNode>();
}

例如:

Dictionary<int, MyNode> nodesDict = new Dictionary<int, MyNode>();
// add the parent node
nodesDict.Add(-1, new TreeNode{NodeData = new MyNode{ID=1, ParentID=0, SeqID=1, Name="Root"}});

// now go through the original list of nodes
foreach (var node in NodesList)
{
    // If there is an entry for this node already in the dictionary,
    // then update its NodeData.
    // Otherwise create a new entry.
    TreeNode dictNode;
    if (nodesDict.TryGetValue(node.ID, out dictNode))
    {
        dictNode.NodeData = node;
    }
    else
    {
        // add this node to the dictionary
        dictNode = new TreeNode{NodeData = node};           
        nodesDict.Add(node.ID, dictNode);
    }

    // find this node's parent, and add the node to the child list
    // if the node's parent doesn't exist, add it
    TreeNode parentNode;
    if (!nodesDict.TryGetValue(node.ParentID, out parentNode))
    {
        // Parent doesn't yet exist.
        // Create an entry for it.
        parentNode = new TreeNode();
        nodesDict.Add(node.ParentID, parentNode);
    }
    // Add this node to the parent's children
    parentNode.Children.Add(node);
}

现在您拥有的是一个字典,其中包含每个树节点的条目,并且树节点知道它们的子节点是什么。此时,您只关心 ID 为 -1 的节点。所有其他节点都可以从那里到达。

var root = nodesDict[-1];
var children = root.Children.OrderBy(n -> n.SeqID);
OutputNodes(children);

OutputNodes方法可以递归地访问children构建平面列表:

List<MyNode> FlatList = new List<MyNode>();

void OutputNodes(IEnumerable<TreeNode> nodes)
{
    foreach (var node in nodes)
    {
        FlatList.Add(node);
        // and then the children
        var orderedChildren = node.Children.OrderBy(n -> n.SeqID);
        OutputNodes(orderedChildren);
    }
}

这是我知道的最好的方法。我不明白你怎么能用 LINQ 做到这一点。

【讨论】:

    【解决方案2】:

    您的树可以概括为m-ary tree。鉴于此,您的平面列表可以通过对该树执行深度优先遍历 (DFT) 来计算。我怀疑这将是最快的,因为 LINQ 不假设您的数据结构具有任何明确的结构。 Here's a quick article on DFT for binary search trees - m-ary 树的 DFT 是对该概念的抽象。

    您的任何状态变量似乎都没有自然排序。您必须想出一些方法来打破孩子之间的联系(例如,为什么在 Parent2 之前访问 Parent1 - 一些比较器告诉算法首先访问 Parent1)。比较可以基于像生成顺序这样简单的东西(例如,最年长的孩子在前)。正如下面评论中提到的,比较 SeqID 以选择下一个要递归的焦点节点。

    【讨论】:

    • 可以通过 SeqID 订购。首先是父母的 SeqID,然后是他们自己的。
    • 父级父级没有 ID,因此它将是例如 -1。在这种情况下,所有的根节点将一个接一个地排序。毕竟,每个孩子都将是地方,而不是在他们的父母之下(每个人都在它自己的父母之下)。这也将在第一级,在第二级和第三级它将不起作用,而且订单不是按 ID,所以如果你说“然后按他们自己的”,它将不起作用。
    • 感谢您教会了我一些新东西。我知道有sary,但不知道m-ary! (+1)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 1970-01-01
    • 2012-09-16
    • 2011-06-22
    • 1970-01-01
    相关资源
    最近更新 更多