简体   繁体   English

C#中的树形数据结构

[英]Tree data structure in C#

I was looking for a tree or graph data structure in C#, but I guess there isn't one provided.我在 C# 中寻找树或图形数据结构,但我想没有提供。 An Extensive Examination of Data Structures Using C# 2.0 a bit about why.使用 C# 2.0 对数据结构进行广泛检查,了解原因。 Is there a convenient library which is commonly used to provide this functionality?是否有一个方便的库通常用于提供此功能? Perhaps through a strategy pattern to solve the issues presented in the article.或许可以通过一种策略模式来解决文章中提出的问题。

I feel a bit silly implementing my own tree, just as I would implementing my own ArrayList.我觉得实现自己的树有点傻,就像实现自己的 ArrayList 一样。

I just want a generic tree which can be unbalanced.我只想要一个可以不平衡的通用树。 Think of a directory tree.想想目录树。 C5 looks nifty, but their tree structures seem to be implemented as balanced red-black trees better suited to search than representing a hierarchy of nodes. C5看起来很漂亮,但它们的树结构似乎被实现为平衡的红黑树,比表示节点层次结构更适合搜索。

My best advice would be that there is no standard tree data structure because there are so many ways you could implement it that it would be impossible to cover all bases with one solution.我最好的建议是没有标准的树数据结构,因为有很多方法可以实现它,不可能用一种解决方案覆盖所有基础。 The more specific a solution, the less likely it is applicable to any given problem.解决方案越具体,它就越不可能适用于任何给定的问题。 I even get annoyed with LinkedList - what if I want a circular linked list?我什至对 LinkedList 感到恼火——如果我想要一个循环链表怎么办?

The basic structure you'll need to implement will be a collection of nodes, and here are some options to get you started.您需要实现的基本结构将是节点的集合,这里有一些选项可以帮助您入门。 Let's assume that the class Node is the base class of the entire solution.假设 class 节点是整个解决方案的基础 class。

If you need to only navigate down the tree, then a Node class needs a List of children.如果您只需要向下导航树,那么节点 class 需要一个子列表。

If you need to navigate up the tree, then the Node class needs a link to its parent node.如果您需要向上导航树,则节点 class 需要指向其父节点的链接。

Build an AddChild method that takes care of all the minutia of these two points and any other business logic that must be implemented (child limits, sorting the children, etc.)构建一个 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);
    }
}

Simple recursive implementation... < 40 lines of code... You just need to keep a reference to the root of the tree outside of the class, or wrap it in another class, maybe rename to TreeNode??简单的递归实现... < 40 行代码... 您只需要在 class 之外保留对树根的引用,或者将其包装在另一个 class 中,也许重命名为 TreeNode?

Here's mine, which is very similar to Aaron Gage's , just a little more conventional, in my opinion.在我看来,这是我的,与Aaron Gage 的非常相似,只是更传统一点。 For my purposes, I haven't ran into any performance issues with List<T> .出于我的目的,我没有遇到List<T>的任何性能问题。 It would be easy enough to switch to a LinkedList if needed.如果需要,切换到 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()));
        }
    }
}

Yet another tree structure:另一个树结构:

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
}

Sample usage:示例用法:

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");
    }
}

BONUS奖金
See fully-fledged tree with:查看成熟的树:

  • iterator迭代器
  • searching搜索
  • Java/C# Java/C#

https://github.com/gt4dev/yet-another-tree-structure https://github.com/gt4dev/yet-another-tree-structure

The generally excellent C5 Generic Collection Library has several different tree-based data structures, including sets, bags and dictionaries.一般优秀的C5 Generic Collection Library有几种不同的基于树的数据结构,包括集合、包和字典。 Source code is available if you want to study their implementation details.如果您想研究它们的实现细节,可以使用源代码。 (I have used C5 collections in production code with good results, although I haven't used any of the tree structures specifically.) (我在生产代码中使用了 C5 collections,效果很好,尽管我没有专门使用任何树结构。)

See https://github.com/YaccConstructor/QuickGraph (previously http://quickgraph.codeplex.com/ )请参阅https://github.com/YaccConstructor/QuickGraph (以前的 http://quickgraph.codeplex.com/

QuickGraph provides generic directed/undirected graph data structures and algorithms for .NET 2.0 and up. QuickGraph 为 .NET 2.0 及更高版本提供通用的有向/无向图数据结构和算法。 QuickGraph comes with algorithms such as depth-first search , breadth-first search , A* search, shortest path, k-shortest path, maximum flow, minimum spanning tree, least common ancestors, etc... QuickGraph supports MSAGL , GLEE , and Graphviz to render the graphs, serialization to GraphML , etc. QuickGraph 自带深度优先搜索广度优先搜索、A* 搜索、最短路径、k-最短路径、最大流量、最小生成树、最小共同祖先等算法... QuickGraph 支持MSAGLGLEEGraphviz渲染图形,序列化到GraphML等。

Here's my own:这是我自己的:

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;
    }
}

Output: Output:

Fastfood
   Pizza
      Margherita
      Marinara
   Burger
      Cheese burger
      Chili burger
      Rice burger

I have a little extension to the solutions.我对解决方案有一点扩展。

Using a recursive generic declaration and a deriving subclass, you can better concentrate on your actual target.使用递归泛型声明和派生子类,您可以更好地专注于您的实际目标。

Notice, it's different from a non generic implementation, you don't need to cast 'node' to 'NodeWorker'.请注意,它与非通用实现不同,您不需要将“node”转换为“NodeWorker”。

Here's my example:这是我的例子:

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);
}

If you would like to write your own, you can start with this six-part document detailing effective usage of C# 2.0 data structures and how to go about analyzing your implementation of data structures in C#.如果您想自己编写,您可以从这个由六部分组成的文档开始,详细介绍 C# 2.0 数据结构的有效用法以及如何 go 分析您在 ZD7EFA19FBE7D39732FD5ADB6D24Z2 中的数据结构实现。 Each article has examples and an installer with samples you can follow along with.每篇文章都有示例和一个安装程序,其中包含您可以跟随的示例。

“An Extensive Examination of Data Structures Using C# 2.0” by Scott Mitchell“使用 C# 2.0 对数据结构进行广泛检查” ,作者:Scott Mitchell

Try this simple sample.试试这个简单的示例。

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
}

I created a Node<T> class that could be helpful for other people.我创建了一个可能对其他人有帮助的Node<T> class The class has properties like: class 具有以下属性:

  • Children孩子们
  • Ancestors祖先
  • Descendants子孙
  • Siblings兄弟姐妹
  • Level of the node节点级别
  • Parent家长
  • Root
  • Etc.等等。

There is also the possibility to convert a flat list of items with an Id and a ParentId to a tree.还可以将具有 Id 和 ParentId 的项目的平面列表转换为树。 The nodes holds a reference to both the children and the parent, so that makes iterating nodes quite fast.节点包含对子节点和父节点的引用,因此迭代节点非常快。

There is the now released .NET codebase: specifically the code for a SortedSet that implements a red-black tree : sortedset.cs现在发布了 .NET 代码库:特别是实现红黑树SortedSet的代码: sortedset.cs

This is, however, a balanced tree structure.然而,这是一个平衡的树结构。 So my answer is more a reference to what I believe is the only native tree-structure in the .NET core library.所以我的回答更多的是参考我认为 .NET 核心库中唯一的原生树结构。

I've completed the code that Berezh has shared .我已经完成了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;
    }
}

Most trees are formed by the data you are processing.大多数树是由您正在处理的数据形成的。

Say you have a person class that includes details of someone's parents , would you rather have the tree structure as part of your “domain class”, or use a separate tree class that contained links to your person objects?假设您有一个person class ,其中包含某人parents的详细信息,您宁愿将树结构作为“域类”的一部分,还是使用包含指向您的人对象的链接的单独树 class? Think about a simple operation like getting all the grandchildren of a person , should this code be in the person class, or should the user of the person class have to know about a separate tree class?考虑一个简单的操作,比如获取一个person的所有grandchildren ,这个代码应该在person class 中,还是person class 的用户必须知道单独的树 class?

Another example is a parse tree in a compiler…另一个例子是编译器中的解析树……

Both of these examples show that the concept of a tree is part of the domain of the data and using a separate general-purpose tree at least doubles the number of objects that are created as well as making the API harder to program again.这两个示例都表明树的概念是数据的一部分,使用单独的通用树至少会使创建的对象数量增加一倍,并使 API 更难再次编程。

We want a way to reuse the standard tree operations, without having to reimplement them for all trees, while at the same time, not having to use a standard tree class.我们想要一种重用标准树操作的方法,而不必为所有树重新实现它们,同时不必使用标准树 class。 Boost has tried to solve this type of problem for C++, but I am yet to see any effect for .NET to get it adapted. Boost已尝试为 C++ 解决此类问题,但我尚未看到 .NET 对其进行调整的任何效果。

I was looking for a tree or graph data structure in C# but I guess there isn't one provided.我在 C# 中寻找树或图形数据结构,但我想没有提供。 An Extensive Examination of Data Structures Using C# 2.0 explains a bit about why.使用 C# 2.0 对数据结构进行广泛检查解释了一些原因。 Is there a convenient library which is commonly used to provide this functionality?是否有一个方便的库通常用于提供此功能? Perhaps through a strategy pattern to solve the issues presented in the article.或许可以通过一种策略模式来解决文章中提出的问题。

I feel a bit silly implementing my own tree, just as I would implementing my own ArrayList.我觉得实现自己的树有点傻,就像实现自己的 ArrayList 一样。

I just want a generic tree which can be unbalanced.我只想要一个可以不平衡的通用树。 Think of a directory tree.想想目录树。 C5 looks nifty, but their tree structures seem to be implemented as balanced red-black trees better suited to search than representing a hierarchy of nodes. C5 看起来很漂亮,但它们的树结构似乎被实现为平衡的红黑树,比表示节点层次结构更适合搜索。

I have added a complete solution and example using the NTree class above.我使用上面的 NTree class 添加了一个完整的解决方案和示例。 I also added the "AddChild" method...我还添加了“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;
        }
    }

Using it使用它

 NTree<KeyValuePair<string, string>> ret = null;
 tree.Traverse(tree, DelegateMethod, node["categoryId"].InnerText, ref ret);

There is also the possibility to use XML with LINQ :还可以将 XML 与LINQ一起使用:

Create XML tree in C# (LINQ to XML) 在 C# (LINQ to XML) 中创建 XML 树

XML is the most mature and flexible solution when it comes to using trees and LINQ provides you with all the tools that you need. XML 是使用树时最成熟、最灵活的解决方案,LINQ 为您提供所需的所有工具。 The configuration of your tree also gets much cleaner and user-friendly as you can simply use an XML file for the initialization.树的配置也变得更加简洁和用户友好,因为您可以简单地使用 XML 文件进行初始化。

If you need to work with objects, you can use XML serialization:如果需要处理对象,可以使用 XML 序列化:

XML serialization XML序列化

Here is my implementation of a BST :这是我的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();
    }
}

If you are going to display this tree on the GUI, you can use TreeView and TreeNode .如果要在 GUI 上显示此树,可以使用TreeViewTreeNode (I suppose technically you can create a TreeNode without putting it on a GUI, but it does have more overhead than a simple homegrown TreeNode implementation.) (我想从技术上讲,您可以创建一个 TreeNode 而无需将其放在 GUI 上,但它确实比简单的本土 TreeNode 实现具有更多开销。)

Tree With Generic Data具有通用数据的树

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public class Tree<T>
{
    public T Data { get; set; }
    public LinkedList<Tree<T>> Children { get; set; } = new LinkedList<Tree<T>>();
    public Task Traverse(Func<T, Task> actionOnNode, int maxDegreeOfParallelism = 1) => Traverse(actionOnNode, new SemaphoreSlim(maxDegreeOfParallelism, maxDegreeOfParallelism));
    private async Task Traverse(Func<T, Task> actionOnNode, SemaphoreSlim semaphore)
    {
        await actionOnNode(Data);
        SafeRelease(semaphore);
        IEnumerable<Task> tasks = Children.Select(async input =>
        {
            await semaphore.WaitAsync().ConfigureAwait(false);
            try
            {
                await input.Traverse(actionOnNode, semaphore).ConfigureAwait(false);
            }
            finally
            {
                SafeRelease(semaphore);
            }
        });
        await Task.WhenAll(tasks);
    }
    private void SafeRelease(SemaphoreSlim semaphore)
    {
        try
        {
            semaphore.Release();
        }
        catch (Exception ex)
        {
            if (ex.Message.ToLower() != "Adding the specified count to the semaphore would cause it to exceed its maximum count.".ToLower())
            {
                throw;
            }
        }
    }

    public async Task<IEnumerable<T>> ToList()
    {
        ConcurrentBag<T> lst = new ConcurrentBag<T>();
        await Traverse(async (data) => lst.Add(data));
        return lst;
    }
    public async Task<int> Count() => (await ToList()).Count();
}



Unit Tests单元测试

using System.Threading.Tasks;
using Xunit;

public class Tree_Tests
{
    [Fact]
    public async Task Tree_ToList_Count()
    {
        Tree<int> head = new Tree<int>();

        Assert.NotEmpty(await head.ToList());
        Assert.True(await head.Count() == 1);

        // child
        var child = new Tree<int>();
        head.Children.AddFirst(child);
        Assert.True(await head.Count() == 2);
        Assert.NotEmpty(await head.ToList());

        // grandson
        child.Children.AddFirst(new Tree<int>());
        child.Children.AddFirst(new Tree<int>());
        Assert.True(await head.Count() == 4);
        Assert.NotEmpty(await head.ToList());
    }

    [Fact]
    public async Task Tree_Traverse()
    {
        Tree<int> head = new Tree<int>() { Data = 1 };

        // child
        var child = new Tree<int>() { Data = 2 };
        head.Children.AddFirst(child);

        // grandson
        child.Children.AddFirst(new Tree<int>() { Data = 3 });
        child.Children.AddLast(new Tree<int>() { Data = 4 });

        int counter = 0;
        await head.Traverse(async (data) => counter += data);
        Assert.True(counter == 10);

        counter = 0;
        await child.Traverse(async (data) => counter += data);
        Assert.True(counter == 9);

        counter = 0;
        await child.Children.First!.Value.Traverse(async (data) => counter += data);
        Assert.True(counter == 3);

        counter = 0;
        await child.Children.Last!.Value.Traverse(async (data) => counter += data);
        Assert.True(counter == 4);
    }
}

I don't like a tree aproach.我不喜欢树的方法。 It gets things overcomplicated including search or dril-down or even ui controls populating.它使事情变得过于复杂,包括搜索或向下钻取,甚至填充 ui 控件。

I would suggest to use a very simple approach with IDictionary<TChild, TParent> .我建议对IDictionary<TChild, TParent>使用一种非常简单的方法。 This also allows to have no connections between nodes or levels.这也允许在节点或级别之间没有连接。

In case you need a rooted tree data structure implementation that uses less memory, you can write your Node class as follows (C++ implementation):如果您需要使用较少 memory 的有根树数据结构实现,您可以按如下方式编写节点 class(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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM