C# 遍历树时查找节点
我想实现一种方法,使我能够在树中找到一个节点。我这样做的方式是递归地使用全局变量来知道何时停止 我上课了:C# 遍历树时查找节点,c#,linq,data-structures,tree,C#,Linq,Data Structures,Tree,我想实现一种方法,使我能够在树中找到一个节点。我这样做的方式是递归地使用全局变量来知道何时停止 我上课了: class Node // represents a node in the tree { // constructor public Node() { Children = new List<Node>(); } public List<Node> Children; public
class Node // represents a node in the tree
{
// constructor
public Node() {
Children = new List<Node>();
}
public List<Node> Children;
public string Name;
public string Content;
}
private bool IsNodeFound = false; // global variable that I use to decide when to stop
// method to find a particular node in the tree
private void Find(Node node, string stringToFind, Action<Node> foundNode)
{
if(IsNodeFound)
return;
if (node.Content.Contains(stringToFind)){
foundNode(node);
IsNodeFound =true;
}
foreach (var child in node.Children)
{
if (child.Content.Contains(stringToFind)){
foundNode(node);
IsNodeFound =true;
}
Find(child, stringToFind, foundNode);
}
}
// root is a node that contain children and those children also contain children
// root is the "root" of the tree
IsNodeFound =false;
Node nodeToFind = null;
Find(root, "some string to look for", (x)=> nodeToFind=x);
所以我的问题是如何使这种方法更加优雅。我希望该方法的签名如下所示:
public Node FindNode(Node rootNode);
我认为这是多余的我在做什么,可能有一个更好的方法来创建该方法。或者,我可以修改节点类,以便通过linq查询实现相同的功能。考虑使用类似linq的API:split“Find”和“Act”部分使其简单化。对于“Act”部分,您甚至不需要任何特殊的自定义代码,现有的LINQ就可以了
public IEnumerable<Node> Where(Func<Node, bool> condition);
public IEnumerable其中(Func条件);
根据您的需要,您可以遍历整个树一次,并检查每个节点以实现Where,或者使用惰性迭代正确地执行它。对于延迟迭代,您需要某种能够记住当前位置的结构(即要访问的节点堆栈和子节点索引)
注意:请避免使用全局变量。也就是说,在当前代码中,从Find函数返回true/false并在返回true时停止迭代是更好的方法。我会这样做: 编写一个实例方法来生成节点的子树(如果不控制
节点
类,则可以将其作为扩展):
您可以使用使用Linq的其他答案之一,也可以使用使用递归的深度优先搜索机制:
public Node Find(string stringToFind)
{
// find the string, starting with the current instance
return Find(this, stringToFind);
}
// Search for a string in the specified node and all of its children
public Node Find(Node node, string stringToFind)
{
if (node.Content.Contains(stringToFind))
return node;
foreach (var child in node.Children)
{
var result = Find(child, stringToFind);
if (result != null)
return result;
}
return null;
}
如果linq的答案让你和我一样困惑,下面我将用简单的递归来实现它。请注意,它是深度优先的,如果这对您的模型更有意义,您可能希望将其更改为广度优先
public Node FindNode(Node rootNode)
{
if (rootNode.Content.Contains(stringToFind))
return rootNode;
foreach (Node node in rootNode.Children)
{
if (node.Content.Contains(stringToFind))
return node;
else
return FindNode(node);
}
return null;
}
您可以使用使用递归的深度优先搜索(不需要全局变量知道何时终止): 或者,如果希望避免递归,可以使用堆栈非递归地完成相同的任务:
Node FindNode2( Node rootNode, string stringToFind ) {
var stack = new Stack<Node>( new[] { rootNode } );
while( stack.Any() ) {
var n = stack.Pop();
if( n.Content == stringToFind ) return n;
foreach( var child in n.Children ) stack.Push( child );
}
return null;
}
节点FindNode2(节点rootNode,字符串stringToFind){
var stack=newstack(new[]{rootNode});
while(stack.Any()){
var n=stack.Pop();
if(n.Content==stringToFind)返回n;
foreach(n.Children中的var child)stack.Push(child);
}
返回null;
}
递归和PLinq
private Node Find(Node node, Func<Node, bool> predicate)
{
if (predicate(node))
return node;
foreach (var n in node.Children.AsParallel())
{
var found = Find(n, predicate);
if (found != default(Node))
return found;
}
return default(Node);
}
完全同意。虽然基于LINQ的解决方案在很多情况下都很漂亮,但简化代码并明确其意图,我相信这一点恰恰相反。+1这是一个伟大的原因,因为我可以根据任何标准进行筛选,例如:
root.GetSubTree().FirstOrDefault(x=>x.Name==“Foo”)代码>非常感谢!如此清晰、准确的回答。
Node FindNode1( Node rootNode, string stringToFind ) {
if( rootNode.Content == stringToFind ) return rootNode;
foreach( var child in rootNode.Children ) {
var n = FindNode1( child, stringToFind );
if( n != null ) return n;
}
return null;
}
Node FindNode2( Node rootNode, string stringToFind ) {
var stack = new Stack<Node>( new[] { rootNode } );
while( stack.Any() ) {
var n = stack.Pop();
if( n.Content == stringToFind ) return n;
foreach( var child in n.Children ) stack.Push( child );
}
return null;
}
private Node Find(Node node, Func<Node, bool> predicate)
{
if (predicate(node))
return node;
foreach (var n in node.Children.AsParallel())
{
var found = Find(n, predicate);
if (found != default(Node))
return found;
}
return default(Node);
}
var found = Find(root, (n) => n.Content.Contains("3"));
if (found != default(Node))
Console.Write("found '{0}'", found.Name);
else Console.Write("not found");