从树顺序排序的树创建平面列表

时间:2014-12-03 14:31:47

标签: c# algorithm linq tree

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

每个节点有4个属性:
* ID
* ParentID,
* Seq
*姓名

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,姓名=“孙子1.2.1”
  • Parent2的ID = 345,ParentID = -1,SeqID = 2,Name =“Parent2”
  • Child2.3,ID = 785,ParentID = 345,SeqID = 3,Name =“Child 2.3”
  • 孙子2.3.1的ID = 854,ParentID = 785,SeqID = 1,Name =“grandson 2.3.1”

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

谢谢!

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

你可以两次通过。首先,您创建一个节点字典,每个节点都有一个子列表。

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方法可以递归访问子节点来构建平面列表:

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做到这一点。