我有一个从这个类创建的树。
class Node
{
public string Key { get; }
public List<Node> Children { get; }
}
我想在所有孩子及其所有孩子中搜索符合条件的孩子:
node.Key == SomeSpecialKey
我该如何实施?
答案 0 :(得分:152)
这是一种误解,这需要递归。 将需要堆栈或队列,最简单的方法是使用递归来实现它。为了完整起见,我将提供一个非递归的答案。
static IEnumerable<Node> Descendants(this Node root)
{
var nodes = new Stack<Node>(new[] {root});
while (nodes.Any())
{
Node node = nodes.Pop();
yield return node;
foreach (var n in node.Children) nodes.Push(n);
}
}
例如,使用此表达式来使用它:
root.Descendants().Where(node => node.Key == SomeSpecialKey)
答案 1 :(得分:16)
Searching a Tree of Objects with Linq
public static class TreeToEnumerableEx
{
public static IEnumerable<T> AsDepthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
{
yield return head;
foreach (var node in childrenFunc(head))
{
foreach (var child in AsDepthFirstEnumerable(node, childrenFunc))
{
yield return child;
}
}
}
public static IEnumerable<T> AsBreadthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
{
yield return head;
var last = head;
foreach (var node in AsBreadthFirstEnumerable(head, childrenFunc))
{
foreach (var child in childrenFunc(node))
{
yield return child;
last = child;
}
if (last.Equals(node)) yield break;
}
}
}
答案 2 :(得分:14)
如果你想保持Linq之类的语法,你可以使用一种方法来获取所有的后代(儿童+孩子的孩子等)。
static class NodeExtensions
{
public static IEnumerable<Node> Descendants(this Node node)
{
return node.Children.Concat(node.Children.SelectMany(n => n.Descendants()));
}
}
然后可以像使用where或first或者其他任何其他人一样查询这个可枚举。
答案 3 :(得分:4)
您可以尝试使用此扩展方法枚举树节点:
static IEnumerable<Node> GetTreeNodes(this Node rootNode)
{
yield return rootNode;
foreach (var childNode in rootNode.Children)
{
foreach (var child in childNode.GetTreeNodes())
yield return child;
}
}
然后将其与Where()
子句一起使用:
var matchingNodes = rootNode.GetTreeNodes().Where(x => x.Key == SomeSpecialKey);
答案 4 :(得分:2)
也许你只需要
node.Children.Where(child => child.Key == SomeSpecialKey)
或者,如果您需要深入搜索一个级别,
node.Children.SelectMany(
child => child.Children.Where(child => child.Key == SomeSpecialKey))
如果您需要搜索所有级别,请执行以下操作:
IEnumerable<Node> FlattenAndFilter(Node source)
{
List<Node> l = new List();
if (source.Key == SomeSpecialKey)
l.Add(source);
return
l.Concat(source.Children.SelectMany(child => FlattenAndFilter(child)));
}
答案 5 :(得分:2)
public class Node
{
string key;
List<Node> children;
public Node(string key)
{
this.key = key;
children = new List<Node>();
}
public string Key { get { return key; } }
public List<Node> Children { get { return children; } }
public Node Find(Func<Node, bool> myFunc)
{
foreach (Node node in Children)
{
if (myFunc(node))
{
return node;
}
else
{
Node test = node.Find(myFunc);
if (test != null)
return test;
}
}
return null;
}
}
然后你可以搜索:
Node root = new Node("root");
Node child1 = new Node("child1");
Node child2 = new Node("child2");
Node child3 = new Node("child3");
Node child4 = new Node("child4");
Node child5 = new Node("child5");
Node child6 = new Node("child6");
root.Children.Add(child1);
root.Children.Add(child2);
child1.Children.Add(child3);
child2.Children.Add(child4);
child4.Children.Add(child5);
child5.Children.Add(child6);
Node test = root.Find(p => p.Key == "child6");
答案 6 :(得分:2)
为什么不使用IEnumerable<T>
扩展方法
public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
if (source == null)
{
yield break;
}
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
foreach (var childItem in childResults)
{
yield return childItem;
}
}
}
然后就这样做
var result = nodes.Children.SelectHierarchy(n => n.Children, n => n.Key.IndexOf(searchString) != -1);
答案 7 :(得分:1)
如果没有递归,这将无效。
试试这个:
IEnumerable<Node> GetMatchingNodes(Node parent, string key)
{
var result = new List<Node>();
if(parent.Key == key)
result.Add(parent)
result.AddRange(parent.Children.Where(c => GetMatchingNodes(c, key)));
return result;
}
这样称呼:
Node rootNode = ...;
var allMatchingNodes = GetMatchingNodes(rootNode, key);
答案 8 :(得分:1)
我有一个可以展平任何IEnumerable<T>
的通用扩展方法,并且从该展平的集合中,您可以获得所需的节点。
public static IEnumerable<T> FlattenHierarchy<T>(this T node, Func<T, IEnumerable<T>> getChildEnumerator)
{
yield return node;
if (getChildEnumerator(node) != null)
{
foreach (var child in getChildEnumerator(node))
{
foreach (var childOrDescendant in child.FlattenHierarchy(getChildEnumerator))
{
yield return childOrDescendant;
}
}
}
}
像这样使用:
var q = from node in myTree.FlattenHierarchy(x => x.Children)
where node.Key == "MyKey"
select node;
var theNode = q.SingleOrDefault();
答案 9 :(得分:0)
前段时间我写了一篇代码项目文章,描述了如何使用Linq查询树状结构:
http://www.codeproject.com/KB/linq/LinqToTree.aspx
这提供了一个linq-to-XML样式的API,您可以在其中搜索后代,子项,祖先等......
对于您当前的问题可能有些过分,但其他人可能会感兴趣。
答案 10 :(得分:0)
您可以使用此扩展方法查询树。
public static IEnumerable<Node> InTree(this Node treeNode)
{
yield return treeNode;
foreach (var childNode in treeNode.Children)
foreach (var flattendChild in InTree(childNode))
yield return flattendChild;
}
答案 11 :(得分:0)
我使用以下实现来枚举Tree items
public static IEnumerable<Node> DepthFirstUnfold(this Node root) =>
ObjectAsEnumerable(root).Concat(root.Children.SelectMany(DepthFirstUnfold));
public static IEnumerable<Node> BreadthFirstUnfold(this Node root) {
var queue = new Queue<IEnumerable<Node>>();
queue.Enqueue(ObjectAsEnumerable(root));
while (queue.Count != 0)
foreach (var node in queue.Dequeue()) {
yield return node;
queue.Enqueue(node.Children);
}
}
private static IEnumerable<T> ObjectAsEnumerable<T>(T obj) {
yield return obj;
}
上面的实现中的BreadthFirstUnfold使用节点序列队列而不是节点队列。这不是经典的BFS算法方式。
答案 12 :(得分:0)
出于娱乐目的(大约十年后),它也使用泛型,但基于@vidstige接受的答案,但具有Stack and While循环。
public static class TypeExtentions
{
public static IEnumerable<T> Descendants<T>(this T root, Func<T, IEnumerable<T>> selector)
{
var nodes = new Stack<T>(new[] { root });
while (nodes.Any())
{
T node = nodes.Pop();
yield return node;
foreach (var n in selector(node)) nodes.Push(n);
}
}
public static IEnumerable<T> Descendants<T>(this IEnumerable<T> encounter, Func<T, IEnumerable<T>> selector)
{
var nodes = new Stack<T>(encounter);
while (nodes.Any())
{
T node = nodes.Pop();
yield return node;
if (selector(node) != null)
foreach (var n in selector(node))
nodes.Push(n);
}
}
}
给出一个可以像这样使用的集合
var myNode = ListNodes.Descendants(x => x.Children).Where(x => x.Key == SomeKey);
或带有根对象
var myNode = root.Descendants(x => x.Children).Where(x => x.Key == SomeKey);