我在C#中寻找树或图的数据结构,但我猜没有提供。使用C#2.0进行的数据结构的广泛检查解释了一些原因。是否有一个方便使用的库通常用于提供此功能?也许可以通过一种策略模式来解决本文中提出的问题。
我实现自己的树有点愚蠢,就像实现自己的ArrayList一样。
我只想要可以不平衡的通用树。想一想目录树。C5看起来很漂亮,但是它们的树结构似乎被实现为平衡的红黑树,比表示节点的层次结构更适合于搜索。
我在C#中寻找树或图的数据结构,但我猜没有提供。使用C#2.0进行的数据结构的广泛检查解释了一些原因。是否有一个方便使用的库通常用于提供此功能?也许可以通过一种策略模式来解决本文中提出的问题。
我实现自己的树有点愚蠢,就像实现自己的ArrayList一样。
我只想要可以不平衡的通用树。想一想目录树。C5看起来很漂亮,但是它们的树结构似乎被实现为平衡的红黑树,比表示节点的层次结构更适合于搜索。
Answers:
我最好的建议是,没有标准的树数据结构,因为您可以通过多种方法来实现它,以至于不可能用一种解决方案覆盖所有基础。解决方案越具体,就越不可能适用于任何给定的问题。我什至对LinkedList感到恼火-如果我想要一个循环链表怎么办?
您需要实现的基本结构将是节点的集合,这是一些入门的选项。假设Node类是整个解决方案的基类。
如果只需要在树上导航,则Node类需要一个子级列表。
如果需要在树上导航,则Node类需要一个指向其父节点的链接。
构建一个AddChild方法,该方法处理这两点的所有细节以及必须实现的任何其他业务逻辑(子级限制,对子级进行排序等)
delegate void TreeVisitor<T>(T nodeData);
class NTree<T>
{
private T data;
private LinkedList<NTree<T>> children;
public NTree(T data)
{
this.data = data;
children = new LinkedList<NTree<T>>();
}
public void AddChild(T data)
{
children.AddFirst(new NTree<T>(data));
}
public NTree<T> GetChild(int i)
{
foreach (NTree<T> n in children)
if (--i == 0)
return n;
return null;
}
public void Traverse(NTree<T> node, TreeVisitor<T> visitor)
{
visitor(node.data);
foreach (NTree<T> kid in node.children)
Traverse(kid, visitor);
}
}
简单的递归实现... <40行代码...您只需要在类外部保留对树根的引用,或将其包装在另一个类中,也许重命名为TreeNode?
Action<T>
委托:public void traverse(NTree<T> node, Action<T> visitor)
。Action <>的签名是:void Action<T>( T obj )
。还有从0到4个不同参数的版本。还有一个类似的函数委托,称为Func<>
。
--i == 0
仅在单个情况下起作用?这是真的。这让我感到困惑
我认为这是我的,与Aaron Gage的非常相似。就我的目的而言,我还没有遇到任何性能问题List<T>
。如果需要的话,切换到LinkedList很容易。
namespace Overby.Collections
{
public class TreeNode<T>
{
private readonly T _value;
private readonly List<TreeNode<T>> _children = new List<TreeNode<T>>();
public TreeNode(T value)
{
_value = value;
}
public TreeNode<T> this[int i]
{
get { return _children[i]; }
}
public TreeNode<T> Parent { get; private set; }
public T Value { get { return _value; } }
public ReadOnlyCollection<TreeNode<T>> Children
{
get { return _children.AsReadOnly(); }
}
public TreeNode<T> AddChild(T value)
{
var node = new TreeNode<T>(value) {Parent = this};
_children.Add(node);
return node;
}
public TreeNode<T>[] AddChildren(params T[] values)
{
return values.Select(AddChild).ToArray();
}
public bool RemoveChild(TreeNode<T> node)
{
return _children.Remove(node);
}
public void Traverse(Action<T> action)
{
action(Value);
foreach (var child in _children)
child.Traverse(action);
}
public IEnumerable<T> Flatten()
{
return new[] {Value}.Concat(_children.SelectMany(x => x.Flatten()));
}
}
}
public TreeNode<T> InsertChild(TreeNode<T> parent, T value) { var node = new TreeNode<T>(value) { Parent = parent }; parent._children.Add(node); return node; }
var five = myTree.AddChild(5); myTree.InsertChild(five, 55);
另一个树结构:
public class TreeNode<T> : IEnumerable<TreeNode<T>>
{
public T Data { get; set; }
public TreeNode<T> Parent { get; set; }
public ICollection<TreeNode<T>> Children { get; set; }
public TreeNode(T data)
{
this.Data = data;
this.Children = new LinkedList<TreeNode<T>>();
}
public TreeNode<T> AddChild(T child)
{
TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
this.Children.Add(childNode);
return childNode;
}
... // for iterator details see below link
}
用法示例:
TreeNode<string> root = new TreeNode<string>("root");
{
TreeNode<string> node0 = root.AddChild("node0");
TreeNode<string> node1 = root.AddChild("node1");
TreeNode<string> node2 = root.AddChild("node2");
{
TreeNode<string> node20 = node2.AddChild(null);
TreeNode<string> node21 = node2.AddChild("node21");
{
TreeNode<string> node210 = node21.AddChild("node210");
TreeNode<string> node211 = node21.AddChild("node211");
}
}
TreeNode<string> node3 = root.AddChild("node3");
{
TreeNode<string> node30 = node3.AddChild("node30");
}
}
奖金
请参阅具有以下特征的成熟树:
node
来的?这是否意味着我必须遍历树才能使用搜索代码?
IEnumerable<>
成员,因此无法编译。
通常来说非常出色的C5通用收藏库具有几种不同的基于树的数据结构,包括集合,包装袋和字典。如果您想研究其实现细节,可以使用源代码。(尽管我没有专门使用任何树结构,但我在生产代码中使用了C5集合,效果很好。)
见http://quickgraph.codeplex.com/
QuickGraph为.Net 2.0及更高版本提供通用的有向/无向图数据结构和算法。QuickGraph带有诸如深度优先搜索,呼吸优先搜索,A *搜索,最短路径,k最短路径,最大流量,最小生成树,最不常见祖先等算法,... QuickGraph支持MSAGL,GLEE和Graphviz渲染图,序列化为GraphML等...
如果您想编写自己的文档,则可以从这份由六部分组成的文档开始,详细介绍C#2.0数据结构的有效用法以及如何分析C#中数据结构的实现。每篇文章都有示例和安装程序,并提供了示例。
Scott Mitchell的“使用C#2.0的数据结构的广泛检查”
我对解决方案有一点扩展。
使用递归泛型声明和派生子类,您可以更好地专注于实际目标。
注意,它与非通用实现不同,您无需在“ NodeWorker”中强制转换“ node”。
这是我的示例:
public class GenericTree<T> where T : GenericTree<T> // recursive constraint
{
// no specific data declaration
protected List<T> children;
public GenericTree()
{
this.children = new List<T>();
}
public virtual void AddChild(T newChild)
{
this.children.Add(newChild);
}
public void Traverse(Action<int, T> visitor)
{
this.traverse(0, visitor);
}
protected virtual void traverse(int depth, Action<int, T> visitor)
{
visitor(depth, (T)this);
foreach (T child in this.children)
child.traverse(depth + 1, visitor);
}
}
public class GenericTreeNext : GenericTree<GenericTreeNext> // concrete derivation
{
public string Name {get; set;} // user-data example
public GenericTreeNext(string name)
{
this.Name = name;
}
}
static void Main(string[] args)
{
GenericTreeNext tree = new GenericTreeNext("Main-Harry");
tree.AddChild(new GenericTreeNext("Main-Sub-Willy"));
GenericTreeNext inter = new GenericTreeNext("Main-Inter-Willy");
inter.AddChild(new GenericTreeNext("Inter-Sub-Tom"));
inter.AddChild(new GenericTreeNext("Inter-Sub-Magda"));
tree.AddChild(inter);
tree.AddChild(new GenericTreeNext("Main-Sub-Chantal"));
tree.Traverse(NodeWorker);
}
static void NodeWorker(int depth, GenericTreeNext node)
{ // a little one-line string-concatenation (n-times)
Console.WriteLine("{0}{1}: {2}", String.Join(" ", new string[depth + 1]), depth, node.Name);
}
这是我自己的:
class Program
{
static void Main(string[] args)
{
var tree = new Tree<string>()
.Begin("Fastfood")
.Begin("Pizza")
.Add("Margherita")
.Add("Marinara")
.End()
.Begin("Burger")
.Add("Cheese burger")
.Add("Chili burger")
.Add("Rice burger")
.End()
.End();
tree.Nodes.ForEach(p => PrintNode(p, 0));
Console.ReadKey();
}
static void PrintNode<T>(TreeNode<T> node, int level)
{
Console.WriteLine("{0}{1}", new string(' ', level * 3), node.Value);
level++;
node.Children.ForEach(p => PrintNode(p, level));
}
}
public class Tree<T>
{
private Stack<TreeNode<T>> m_Stack = new Stack<TreeNode<T>>();
public List<TreeNode<T>> Nodes { get; } = new List<TreeNode<T>>();
public Tree<T> Begin(T val)
{
if (m_Stack.Count == 0)
{
var node = new TreeNode<T>(val, null);
Nodes.Add(node);
m_Stack.Push(node);
}
else
{
var node = m_Stack.Peek().Add(val);
m_Stack.Push(node);
}
return this;
}
public Tree<T> Add(T val)
{
m_Stack.Peek().Add(val);
return this;
}
public Tree<T> End()
{
m_Stack.Pop();
return this;
}
}
public class TreeNode<T>
{
public T Value { get; }
public TreeNode<T> Parent { get; }
public List<TreeNode<T>> Children { get; }
public TreeNode(T val, TreeNode<T> parent)
{
Value = val;
Parent = parent;
Children = new List<TreeNode<T>>();
}
public TreeNode<T> Add(T val)
{
var node = new TreeNode<T>(val, this);
Children.Add(node);
return node;
}
}
输出:
Fastfood
Pizza
Margherita
Marinara
Burger
Cheese burger
Chili burger
Rice burger
试试这个简单的示例。
public class TreeNode<TValue>
{
#region Properties
public TValue Value { get; set; }
public List<TreeNode<TValue>> Children { get; private set; }
public bool HasChild { get { return Children.Any(); } }
#endregion
#region Constructor
public TreeNode()
{
this.Children = new List<TreeNode<TValue>>();
}
public TreeNode(TValue value)
: this()
{
this.Value = value;
}
#endregion
#region Methods
public void AddChild(TreeNode<TValue> treeNode)
{
Children.Add(treeNode);
}
public void AddChild(TValue value)
{
var treeNode = new TreeNode<TValue>(value);
AddChild(treeNode);
}
#endregion
}
因为没有提及,所以我想请您注意现在发布的.net代码库:特别SortedSet
是实现Red-Black-Tree的的代码:
但是,这是一个平衡的树结构。因此,我的答案更多地是参考我认为是.net核心库中唯一的本机树结构。
我已经完成@Berezh共享的代码。
public class TreeNode<T> : IEnumerable<TreeNode<T>>
{
public T Data { get; set; }
public TreeNode<T> Parent { get; set; }
public ICollection<TreeNode<T>> Children { get; set; }
public TreeNode(T data)
{
this.Data = data;
this.Children = new LinkedList<TreeNode<T>>();
}
public TreeNode<T> AddChild(T child)
{
TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
this.Children.Add(childNode);
return childNode;
}
public IEnumerator<TreeNode<T>> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
}
public class TreeNodeEnum<T> : IEnumerator<TreeNode<T>>
{
int position = -1;
public List<TreeNode<T>> Nodes { get; set; }
public TreeNode<T> Current
{
get
{
try
{
return Nodes[position];
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public TreeNodeEnum(List<TreeNode<T>> nodes)
{
Nodes = nodes;
}
public void Dispose()
{
}
public bool MoveNext()
{
position++;
return (position < Nodes.Count);
}
public void Reset()
{
position = -1;
}
}
这是一棵树
public class Tree<T> : List<Tree<T>>
{
public T Data { get; private set; }
public Tree(T data)
{
this.Data = data;
}
public Tree<T> Add(T data)
{
var node = new Tree<T>(data);
this.Add(node);
return node;
}
}
您甚至可以使用初始化程序:
var tree = new Tree<string>("root")
{
new Tree<string>("sample")
{
"console1"
}
};
大多数树由您正在处理的数据组成。
假设您有一个
person
包含某人的详细信息的类,parents
是将树结构作为“域类”的一部分,还是使用一个单独的树类来包含个人对象的链接?考虑一个简单的操作,例如获取a的所有grandchildren
内容person
,此代码应该在person
类中,还是类的用户person
必须知道一个单独的树类?
另一个例子是编译器中的解析树…
这两个示例都表明,树的概念是数据域的一部分,使用单独的通用树至少会使创建的对象数量增加一倍,并使API难以再次编程。
我们想要的是一种重用标准树操作的方法,而不必为所有树重新实现它们,同时又不必使用标准树类。Boost试图解决C ++的这类问题,但我尚未看到适应.NET的任何效果。
我已经使用上面的NTree类添加了完整的解决方案和示例,还添加了“ AddChild”方法...
public class NTree<T>
{
public T data;
public LinkedList<NTree<T>> children;
public NTree(T data)
{
this.data = data;
children = new LinkedList<NTree<T>>();
}
public void AddChild(T data)
{
var node = new NTree<T>(data) { Parent = this };
children.AddFirst(node);
}
public NTree<T> Parent { get; private set; }
public NTree<T> GetChild(int i)
{
foreach (NTree<T> n in children)
if (--i == 0)
return n;
return null;
}
public void Traverse(NTree<T> node, TreeVisitor<T> visitor, string t, ref NTree<T> r)
{
visitor(node.data, node, t, ref r);
foreach (NTree<T> kid in node.children)
Traverse(kid, visitor, t, ref r);
}
}
public static void DelegateMethod(KeyValuePair<string, string> data, NTree<KeyValuePair<string, string>> node, string t, ref NTree<KeyValuePair<string, string>> r)
{
string a = string.Empty;
if (node.data.Key == t)
{
r = node;
return;
}
}
使用
NTree<KeyValuePair<string, string>> ret = null;
tree.Traverse(tree, DelegateMethod, node["categoryId"].InnerText, ref ret);
这是我的BST实现
class BST
{
public class Node
{
public Node Left { get; set; }
public object Data { get; set; }
public Node Right { get; set; }
public Node()
{
Data = null;
}
public Node(int Data)
{
this.Data = (object)Data;
}
public void Insert(int Data)
{
if (this.Data == null)
{
this.Data = (object)Data;
return;
}
if (Data > (int)this.Data)
{
if (this.Right == null)
{
this.Right = new Node(Data);
}
else
{
this.Right.Insert(Data);
}
}
if (Data <= (int)this.Data)
{
if (this.Left == null)
{
this.Left = new Node(Data);
}
else
{
this.Left.Insert(Data);
}
}
}
public void TraverseInOrder()
{
if(this.Left != null)
this.Left.TraverseInOrder();
Console.Write("{0} ", this.Data);
if (this.Right != null)
this.Right.TraverseInOrder();
}
}
public Node Root { get; set; }
public BST()
{
Root = new Node();
}
}
如果需要使用较少内存的根目录树数据结构实现,则可以按以下方式编写Node类(C ++实现):
class Node {
Node* parent;
int item; // depending on your needs
Node* firstChild; //pointer to left most child of node
Node* nextSibling; //pointer to the sibling to the right
}