ChatGPT解决这个技术问题 Extra ChatGPT

C#中的树数据结构

我一直在寻找 C# 中的树或图形数据结构,但我想没有提供。 An Extensive Examination of Data Structures Using C# 2.0 关于原因。是否有一个方便的库通常用于提供此功能?或许通过一个strategy pattern来解决文章中提出的问题。

我觉得实现自己的树有点傻,就像我实现自己的 ArrayList 一样。

我只想要一个可以不平衡的通用树。想想目录树。 C5 看起来很漂亮,但它们的树结构似乎被实现为平衡的红黑树,比表示节点层次结构更适合搜索。

我认为为一个非常简单的树导入整个 UI 库是一个坏主意。
你能激励吗?它不再像实际的硬盘空间需求是一个问题了吗?笨拙?正如我之前提到的,我可以理解这不是针对专业软件或没有现有用户界面的东西的解决方案。我是一个懒惰的程序员,如果我能免费获得一个结构,那就太好了。现有的库确实有很多免费的,可以从使用它的人那里找到很多代码。
这是一个简单的树类型:public class Tree<T> : List<Tree<T>> { public T Value; }
此外,它可能会产生很多兼容性和维护问题。您的程序仅适用于 Windows...仅仅因为您使用了一些 UI 树用于 winforms 或 WPF?如果您想要更新您的软件,但您还依赖于(可能很多)UI 机制的依赖兼容性,会发生什么?

D
David Boike

我最好的建议是没有标准的树数据结构,因为有很多方法可以实现它,不可能用一种解决方案覆盖所有基础。解决方案越具体,它就越不可能适用于任何给定的问题。我什至对 LinkedList 感到恼火——如果我想要一个循环链表怎么办?

您需要实现的基本结构将是节点的集合,这里有一些选项可以帮助您入门。让我们假设类 Node 是整个解决方案的基类。

如果您只需要向下导航树,那么 Node 类需要一个子列表。

如果您需要向上导航树,那么 Node 类需要一个指向其父节点的链接。

构建一个 AddChild 方法来处理这两点的所有细节以及必须实现的任何其他业务逻辑(子限制、对子排序等)


就我个人而言,我不介意将某种自平衡二叉树添加到库中,因为这比仅使用邻接列表需要额外的工作。
@jk 我相信 SortedDictionary 和 SortedSet 是建立在红/黑树之上的,所以使用它们应该可以工作。
看看复合模式 ;-) 正是你要找的
我已经创建了一个库,它可以完成您所说的一切无法标准化的事情。它只是包装了您正在谈论的所有额外位,因此您无需知道它们是如何制作的。 github.com/Unskilledcrab/Hierarchy。循环数据也被处理
S
Steve Morgan
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?


在这种情况下,无论如何,在 C# 中,您可以避免编写自己的委托并使用预制的 Action<T> 委托:public void traverse(NTree<T> node, Action<T> visitor)。 Action<> 的签名是:void Action<T>( T obj )。还有从 0 到 4 个不同参数的版本。还有一个类似的函数委托,称为 Func<>
LinkedList 的优点是它更有效地用于我们将其置于此处的目的,并且它只消耗所需的内存,无论存储多少子节点。唯一对基于数组的 List 实现更有效的操作是 getChild(int),但我希望谨慎调用它,通常会使用 add 和 traverse,LinkedList 非常适合。完成实现并添加 Remove 可能会使事情复杂化。如果 C#s 泛型允许用户指定 List 实现以最适合使用,那就太好了,但事实并非如此。
我怎么称呼这个代表?
将遍历方法更改为静态或可能将其包装以隐藏递归性质将是一个好主意,但遍历很简单:创建一个具有委托签名的方法,即用于整数树: void my_visitor_impl(int datum) - 如果需要,将其设为静态,实例化一个 delgate:TreeVisitor my_visitor = my_visitor_impl;然后在根节点或 NTree 类上调用(如果您将其设为静态): NTree.traverse(my_tree, my_visitor)
使 addChild() 返回它添加的 NTree 可以更好地将数据添加到树中。 (除非我错过了一种巧妙的方法来构建树,而不依赖于新添加的 child == getChild(1) 的实现细节?)
R
Ronnie Overby

这是我的,在我看来,它与 Aaron Gage's 非常相似,只是更传统一点。就我的目的而言,我没有遇到 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()));
        }
    }
}

为什么在构造函数中设置 Value 属性时会暴露它?在您已经通过构造函数设置它之后,它会打开以进行操作吗?应该是私人设置吗?
当然,为什么不让它不可变呢?已编辑。
谢谢!我很喜欢不用自己写。 (仍然无法相信它不是原生存在的东西。我一直认为 .net 或至少 .net 4.0 拥有一切。)
我喜欢这个解决方案。我还发现我需要插入,我添加了以下方法来做到这一点。 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);
这是一段特殊的代码,在我看来是最好的答案。通读它本身就是一场讲座。
G
Grzegorz Dev

另一个树结构:

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

奖金查看成熟的树:

迭代器

搜索

Java/C#

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


如何在您的代码示例中使用搜索? node 来自哪里?这是否意味着我必须遍历树才能使用搜索代码?
@GrzegorzDev 也许是-1,因为它没有实现所有 IEnumerable<> 成员,所以它不会编译。
@UweKeim 干得好,下次尝试将代码与实际用途一起使用。
我看到的唯一问题是它不会被基本的 JsonConvert 正确序列化,因为它实现了 IEnumerable<>
@Grzegorz Dev - 嗨,有没有办法将第二级的所有节点作为字符串列表?
M
McKenzieG1

一般优秀的 C5 Generic Collection Library 有几种不同的基于树的数据结构,包括集合、包和字典。如果您想研究它们的实现细节,可以使用源代码。 (我在生产代码中使用了 C5 集合并取得了很好的效果,尽管我没有专门使用任何树结构。)


不知道事情是否发生了变化,但现在这本书可以从 C5 站点以 PDF 格式免费下载。
缺少文档不再是一个问题,因为有一个 272 页长的 pdf 补充了库......无法评论代码质量,但从文档质量来看,我真的很期待今晚深入研究!
据我了解,这个 C5 库根本没有树,只有一些树派生的数据结构。
n
nietras

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

QuickGraph 为 .NET 2.0 及更高版本提供通用的有向/无向图数据结构和算法。 QuickGraph自带depth-first searchbreadth-first search、A*搜索、最短路径、k-最短路径、最大流量、最小生成树、最小共同祖先等算法... QuickGraph支持MSAGLGLEE , 和 Graphviz 呈现图形,序列化为 GraphML 等。


QuickGraph 链接已损坏:“嗯。我们无法找到该站点。我们无法连接到 quickgraph.codeplex.com 上的服务器。”
m
moien

这是我自己的:

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

P
Peter Mortensen

我对解决方案有一点扩展。

使用递归泛型声明和派生子类,您可以更好地专注于您的实际目标。

请注意,它与非通用实现不同,您不需要将“node”转换为“NodeWorker”。

这是我的例子:

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

什么是深度,你从哪里以及如何得到它?
@WeDoTDD.com 查看他的类,您会看到 Traverse 将其声明为 0 以从根节点开始,然后使用 traverse 方法在每次迭代中添加到该 int 。
您将如何在整个树中搜索特定节点?
B
Berezh

试试这个简单的示例。

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
}

P
Peter Mortensen

我创建了一个可能对其他人有所帮助的 Node<T> class。该类具有以下属性:

孩子们

祖先

子孙

兄弟姐妹

节点级别

家长

等等。

还可以将具有 Id 和 ParentId 的项目的平面列表转换为树。节点包含对子节点和父节点的引用,因此迭代节点非常快。


P
Peter Mortensen

现在发布了 .NET 代码库:特别是实现 red-black treeSortedSet 的代码:sortedset.cs

然而,这是一个平衡的树结构。所以我的回答更多地是对我认为是 .NET 核心库中唯一的本机树结构的参考。


P
Peter Mortensen

我已经完成了 Berezh has shared 的代码。

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

好的设计。但是我不确定节点是否“是”其子节点的序列。我会考虑以下几点:一个节点“有”零个或多个子节点,所以一个节点不是从一系列子节点派生的,而是它的子节点的聚合(组合?)
P
Peter Mortensen

大多数树是由您正在处理的数据形成的。

假设您有一个包含某人父母详细信息的人员类,您希望将树结构作为“域类”的一部分,还是使用包含指向您的人员对象的链接的单独树类?考虑一个简单的操作,比如获取一个人的所有孙子,这段代码应该在 person 类中,还是 person 类的用户必须知道一个单独的树类?

另一个例子是编译器中的 parse tree...

这两个示例都表明,树的概念是数据域的一部分,使用单独的通用树至少会使创建的对象数量增加一倍,并使 API 更难再次编程。

我们想要一种重用标准树操作的方法,而不必为所有树重新实现它们,同时不必使用标准树类。 Boost 已尝试为 C++ 解决此类问题,但我尚未看到 .NET 对其进行调整的任何效果。


@Puchacz,对不起,我已经 15 年没有 C++ 数据了,看看 Boost 和 Templates,经过一些薄弱的研究,你可能会理解它们。电源学习成本高!!
P
Peter Mortensen

我使用上面的 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);

遍历可能是静态方法吗?作为一个将自己传递给自身的实例方法,这似乎很尴尬
F
Florian Lavorel

还可以将 XML 与 LINQ 一起使用:

Create XML tree in C# (LINQ to XML)

在使用树时,XML 是最成熟、最灵活的解决方案,而 LINQ 为您提供了所需的所有工具。树的配置也变得更加简洁和用户友好,因为您可以简单地使用 XML 文件进行初始化。

如果需要处理对象,可以使用 XML 序列化:

XML serialization


这是一个练习法语的好机会,但也许也提供相应的英语?
D
Denise Skidmore

如果要在 GUI 上显示此树,可以使用 TreeViewTreeNode。 (我想从技术上讲,您可以创建一个 TreeNode 而无需将其放在 GUI 上,但它确实比简单的本土 TreeNode 实现具有更多开销。)


P
Peter Mortensen

这是我对 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();
    }
}

B
Bar Nuri

具有通用数据的树

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



单元测试

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


什么单元测试框架? NUnit
一个解释将是有序的。例如,想法/要点是什么? SafeRelease() 的目的是什么?例如,为什么需要 SafeRelease()?线程安全?决定使用 asyncawait 背后的想法是什么?需要的最低 C# 版本是多少?请通过 editing (changing) your answer 回复,而不是在评论中(没有“编辑:”、“更新:”或类似内容 - 答案应该看起来好像是今天写的)。
A
ADM-IT

我不喜欢树的方法。它使事情变得过于复杂,包括搜索或向下钻取,甚至填充 ui 控件。

我建议对 IDictionary<TChild, TParent> 使用一种非常简单的方法。这也允许在节点或级别之间没有连接。


J
Jake

如果您需要使用较少内存的有根树数据结构实现,您可以按如下方式编写 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
}

在专门针对 C# 的问题上发布 C++ 代码并不是最好的主意,Jake。尤其是包含指针的。你知道指针在 C# 中被无情地追捕,对吧? :p
@ThunderGr 这不公平。用 C# 回答会更好,但是那些 C++ 指针可以被 C# 使用者理解为引用(它们不太安全,好吧)。在 David Boike、Aaron Gage、Ronnie Overby、Grzegorz Dev、Berezh 和 Erik Nagel 都提出了基本相同的数据结构,只有表达方式上的细微差别之后,Jake 提出了分解链表以产生一个只有一种节点类型的更简单的结构和同级导航。不要通过否决建设性的答案来表达您对 C++ 的厌恶。
@migle 我没有对答案投反对票(也没有投反对票)。而且我并不讨厌 C++。我看到答案被否决了,没有人向 Jake 提出任何关于他为什么以及如何改进他的答案的建议。这不是关于“变得更好”。该问题仅针对 C# 标记。不建议使用标签以外的其他语言发布答案,有些人会投反对票。