C# 修剪结束节点满足某些条件的树分支

C# 修剪结束节点满足某些条件的树分支,c#,linq,C#,Linq,我有一个TreeNode类,如下所示: public class TreeNode { public enum NodeType { Root,Element, Category} public TreeNode() { Children = new List<TreeNode>(); } public List<TreeNode> Children { get; set; } public string N

我有一个
TreeNode
类,如下所示:

public class TreeNode
{
    public enum NodeType { Root,Element, Category}
    public TreeNode()
    {
        Children = new List<TreeNode>();
    }
    public List<TreeNode> Children { get; set; }
    public string Name { get; set; }
    public NodeType Type { get; set; }
}
之后

Root
|
|_ NodeA (Element)
|_ Node B (Element)
|  |_ Node B.1 (Category)
|_ Node C (Element)
|  |_ Node C.1 (Element)
|    |_Node C.1.1 (Category)
|_ Node D (Element)
   |_Node D.1 (Element)
Root
|
|_ Node B (Element)
|  |_ Node B.1 (Category)
|_ Node C (Element)
   |_ Node C.1 (Element)
     |_Node C.1.1 (Category)

提前谢谢

没有那么复杂;您只需要对树进行递归遍历。基本情况是节点本身是一个类别。然后只需对每个子对象调用函数,并将那些具有类别的子对象保留在它们的子对象中

/// <summary>
/// Removes all child nodes that do not have at least one Category as a child.
/// </summary>
/// <param name="node">The node to alter the children of.</param>
/// <returns>True if there is at least one Category in this subtree</returns>
public static bool RemoveEmptyElements(TreeNode node)
{
    if (node.Type == TreeNode.NodeType.Category)
        return true;
    node.Children = node.Children.Where(child => RemoveEmptyElements(child))
            .ToList();
    return node.Children.Any();
}
//
///删除至少没有一个类别作为子类别的所有子节点。
/// 
///要更改其子级的节点。
///如果此子树中至少有一个类别,则为True
公共静态布尔删除元素(TreeNode节点)
{
if(node.Type==TreeNode.NodeType.Category)
返回true;
node.Children=node.Children.Where(child=>removemptyelements(child))
.ToList();
返回node.Children.Any();
}

您可以使用post order遍历树。当您返回到树的第二级而未找到类别类型叶时。从该节点再次遍历以删除所有根节点的叶子。

首先,我们需要描述如何对树进行深度优先聚合:

//seed: leafNode -> accumulate
//func: interiorNode, children accumulates -> accumulate
public static TAccumulate Aggregate<TAccumulate>(
    this TreeNode root,
    Func<TreeNode, TAccumulate> seed,
    Func<TreeNode, List<TAccumulate>, TAccumulate> func)
{
    if (root.Children == null || !root.Children.Any())
        return seed(root);
    return func(root, children.Select(child => child.Aggregate(seed, func)).ToList());
}
//种子:叶节点->累积
//func:interiorNode,子节点累加->累加
公共静态累积骨料(
这个树根,
Func seed,
Func Func)
{
if(root.Children==null | |!root.Children.Any())
返回种子(根);
返回func(root,children.Select(child=>child.Aggregate(seed,func)).ToList();
}
然后我们可以使用它进行修改,如您要求的修改:

tree.Aggregate(
    leaf => new
    {
        Keep = leaf.NodeType == TreeNode.NodeType.Category,
        Node = leaf
    },
    (node, children) =>
    {
        var removal = new HashSet<TreeNode>(
            children.Where(child => !child.Keep).Select(child => child.Node));
        node.Children.RemoveAll(removal.Contains);
        return new
        {
           Keep = children.Any(child => child.Keep),
           Node = node
        };
    });
tree.Aggregate(
叶=>新的
{
Keep=leaf.NodeType==TreeNode.NodeType.Category,
节点=叶
},
(节点,子节点)=>
{
var removation=newhashset(
child.Where(child=>!child.Keep).Select(child=>child.Node));
node.Children.RemoveAll(removation.Contains);
还新
{
Keep=children.Any(child=>child.Keep),
节点=节点
};
});

这为您提供了一种简洁、声明性的方式来描述要应用于树的转换,而无需在转换描述中详细说明遍历的细节。

您希望从叶节点修剪多少层?我用我想要的可视化表示更新了问题。如果一个有趣/有趣的问题没有以+1类结尾,基本上从根开始修剪整个分支…不需要指定LINQ,但是,人们应该给你解决问题的答案。这至少需要我整个周末才能理解。成功了!谢谢你,蒂莫西·希尔兹。美女,帽子off@AdolfoPerez另外,如果您想重新创建列表,请使用
node.Children=Children.Where(child=>child.Keep.ToList()
,或者
node.Children.RemoveAll(Children.Where(child=>!child.Keep))替换
foreach
如果您不这样做。@Servy
List.RemoveAll
接受一个
谓词,而不是
IEnumerable
。对,您需要:
var badChildren=new HashSet(children.Where(child=>!child.Keep));node.Children.RemoveAll(child=>badChildren.Contains(child))这似乎在@Servy也能正常工作。不幸的是,我已经标出了答案,但谢谢你的帮助!