我在c#中寻找树或图数据结构,但我猜没有提供。使用c# 2.0的数据结构的广泛检查。是否有一个方便的库通常用于提供这个功能?也许可以通过策略模式来解决文章中提出的问题。

我觉得实现我自己的树有点傻,就像实现我自己的数组列表一样。

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


当前回答

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

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

其他回答

我对解做了一些扩展。

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

注意,它不同于非泛型实现,你不需要将'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);
}

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

假设您有一个person类,其中包含某人的细节 父母们,你们愿意让树形结构成为你们的一部分吗 “域类”,或者使用包含链接的单独树类 你的人反对吗?考虑一个简单的操作,比如获取全部 一个人的孙子,应该是这个人的密码 类,或者person类的用户必须知道 单独的树类?

另一个例子是编译器中的解析树…

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

我们想要一种重用标准树操作的方法,而不必为所有树重新实现它们,同时又不必使用标准树类。Boost已经尝试为c++解决这类问题,但是我还没有看到任何对. net进行适应的效果。

具有通用数据的树

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

这是我的,和艾伦·盖奇的很相似,只是在我看来更传统一点。就我而言,我使用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()));
        }
    }
}

通常优秀的C5通用集合库具有几种不同的基于树的数据结构,包括集、包和字典。如果您想研究它们的实现细节,可以使用源代码。(我在产品代码中使用过C5集合,效果很好,尽管我没有特别使用过任何树结构。)