276

I was looking for a tree or graph data structure in C#, but I guess there isn't one provided. An Extensive Examination of Data Structures Using C# 2.0 a bit about why. 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.

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.

9
  • 2
    Bit more extreme trees: stackoverflow.com/questions/196294/… ;-) Jul 20, 2012 at 17:14
  • 13
    I would consider it to be a bad idea to import an entire UI library for a very simple tree.
    – stimms
    Oct 3, 2012 at 19:22
  • 2
    Could you motivate? Its not like actual harddrive space requirement is an issue anymore? Clumsy? As I mentioned before, I can understand that this is not a solution for an specialised software or something without an existing user interface. I am a lazy programmer, if I can get a structure for free its all good. And an existing library does have a lot for free, one can find a lot of code from people that used it for a lot of things.
    – Simply G.
    Oct 4, 2012 at 10:49
  • 3
    Here's a simple tree type: public class Tree<T> : List<Tree<T>> { public T Value; }. Jan 8, 2016 at 14:40
  • 3
    Also, it might creates a lot of compatibility and maintenance issue. Your program is Windows only... just because you used some UI tree for winforms or WPF? What happens if you want to update your software, but you also depend on the (probably lots of) dependencies compatibility of the UI machinery?
    – Pac0
    Jun 15, 2020 at 8:13

20 Answers 20

175

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?

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.

If you need to only navigate down the tree, then a Node class needs a List of children.

If you need to navigate up the tree, then the Node class needs a link to its parent node.

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.)

4
  • 7
    personally i wouldn't mind some sort of self-balancing binary tree to be added to the library as this is some extra work than just using an adjaceny list.
    – jk.
    Jan 6, 2010 at 13:00
  • 9
    @jk I believe that SortedDictionary and SortedSet are built atop red/black trees, so using these should work.
    – jonp
    Sep 24, 2010 at 13:41
  • Take a look at composite pattern ;-) Exactly what you're looking for Oct 12, 2012 at 9:35
  • I've made a library that does everything you are saying can't be standardized. It simply wraps all of the extra bits that you are talking about so that you don't need to know how they are made. github.com/Unskilledcrab/Hierarchy. Cyclic data is also handled Apr 19 at 15:07
132
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??

17
  • 27
    In this case, in C# anyway, you could avoid writing your own delegate and use the pre-made Action<T> delegate: public void traverse(NTree<T> node, Action<T> visitor) . Action<>'s signature is: void Action<T>( T obj ) . There are also versions from 0 to 4 different parameters. There's also an analogous delegate for functions called Func<>. Feb 6, 2010 at 23:45
  • 1
    Advantage of LinkedList is it is more efficient for the purposes we put it to here, and it only consumes as much memory as it needs for however many child nodes are stored. The only action that would be more efficient with an array based List implementation is the getChild(int), but I would expect that to be invoked sparingly, usually add and traverse will be used, for which LinkedList is ideally suited. Completing the implementation and adding in Remove may complicate things. Be nice if C#s generics allowed the user to specify the List implementation to best suit usage, but it doesnt.
    – Aaron Gage
    Mar 9, 2010 at 4:30
  • 2
    how would i call this delegate?
    – Freakishly
    Jan 17, 2011 at 0:54
  • 3
    changing the traverse method to be static or possibly wrapping it to hide the recursive nature would be a good idea, but it is simple to traverse: create a method with the signature of delegate ie for a tree of ints: void my_visitor_impl(int datum) - make it static if you need, instantiate a delgate: TreeVisitor<int> my_visitor = my_visitor_impl; and then invoke on the root node or NTree class if u make it static: NTree<int>.traverse(my_tree, my_visitor)
    – Aaron Gage
    Jan 17, 2011 at 7:01
  • 11
    Making addChild() return the NTree that it added would make it nicer for adding data to a tree. (Unless I'm missing a cunning way to build a tree with this, without relying on the implementation detail that a newly added child == getChild(1)?)
    – Rory
    Aug 16, 2011 at 23:40
69

Here's mine, which is very similar to Aaron Gage's, just a little more conventional, in my opinion. For my purposes, I haven't ran into any performance issues with List<T>. It would be easy enough to switch to a LinkedList if needed.


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()));
        }
    }
}
5
  • why is your Value property exposed when you're setting it in the constructor? that leaves it open for manipulation AFTER you've already set it via constructor right? Should be private set? Mar 29, 2013 at 20:47
  • Sure, why not make it immutable? Edited. Mar 30, 2013 at 4:44
  • 1
    Thanks! I quite liked not having to write my own. (Still can't believe it isn't a thing that exists natively. I always thought .net, or at least .net 4.0, had everything.)
    – neminem
    May 14, 2013 at 17:40
  • 4
    I liked this solution. I also found I needed to insert, I added the following method to do that. 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); Oct 8, 2015 at 16:21
  • 1
    This is an exceptional piece of code and in my opinion the best answer. Reading through it was a lecture in of itself.
    – Dean P
    May 4, 2021 at 20:17
52

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#

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

5
  • How do I use the search in your code example? Where does node come from? Does it mean I have to iterate over the tree in order to use the search code? Oct 1, 2015 at 16:25
  • @GrzegorzDev Maybe -1 because it does not implement all IEnumerable<> members, so it doesn't compile.
    – Uwe Keim
    May 3, 2016 at 6:25
  • 3
    @UweKeim Good Job, next time try using the code with the actual usings.
    – szab.kel
    Aug 21, 2017 at 13:05
  • only problem i see is that it will not be correctly serialized with basic JsonConvert as it implement IEnumerable<>
    – Rakiah
    Jun 18, 2019 at 11:11
  • @Grzegorz Dev - Hi is there a way to get all the nodes at level two as a list of strings?
    – liv2hak
    Sep 17, 2020 at 8:29
24

The generally excellent C5 Generic Collection Library has several different tree-based data structures, including sets, bags and dictionaries. 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.)

3
  • 7
    Don't know if maybe things have changed but right now the book is freely available to download as PDF from the C5 site.
    – Oskar
    Aug 6, 2009 at 12:10
  • 4
    Lack of documentation is no more a concern as there's a 272 pages long pdf complementing the library... Can't comment on code quality, but judging from the doc quality, I'm really looking forward to digging into this tonight! Jan 25, 2010 at 16:00
  • 2
    From what I understand, this C5 library doesn't have trees at all, but only some tree-derived data structures.
    – roim
    Aug 20, 2014 at 12:38
11

See https://github.com/YaccConstructor/QuickGraph (previously http://quickgraph.codeplex.com/)

QuickGraph provides generic directed/undirected graph data structures and algorithms for .NET 2.0 and up. 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.

1
  • The QuickGraph link is broken: "Hmm. We’re having trouble finding that site. We can’t connect to the server at quickgraph.codeplex.com." Dec 5, 2021 at 17:36
10

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:

Fastfood
   Pizza
      Margherita
      Marinara
   Burger
      Cheese burger
      Chili burger
      Rice burger
9

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'.

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);
}
3
  • what is depth and where and how do you get it from? Mar 29, 2013 at 4:49
  • @WeDoTDD.com looking at his class you see Traverse declares it as 0 to start at the root node, then uses the method traverse adding to that int each iteration.
    – Edward
    Sep 30, 2016 at 19:30
  • How would you search the whole tree for a a particular node?
    – mattpm
    Oct 29, 2019 at 4:22
4

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

I created a Node<T> class that could be helpful for other people. The class has properties like:

  • 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. The nodes holds a reference to both the children and the parent, so that makes iterating nodes quite fast.

1
2

There is the now released .NET codebase: specifically the code for a SortedSet that implements a red-black tree: 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.

2

I've completed the code that 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;
    }
}
1
  • Good design. However I'm not sure if a node 'is' a sequence of its child node. I'd consider the following: a node 'has' zero or more child nodes, so a node is not derived from a sequence of child nodes, but it is an aggregation (composition?) of its child nodes Oct 12, 2016 at 9:01
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? 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?

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.

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. 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.

1
  • @Puchacz, sorry I am 15 years out of data on C++, have a look at Boost and Templates, after a few weak of study you may understand them. Power has high learning costs!! Nov 18, 2016 at 9:59
1

I have added a complete solution and example using the NTree class above. I also added the "AddChild" method...

    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);
1
  • Should traverse maybe be a static method? It seems very awkward as a n instance method passing itself into itself May 30, 2018 at 21:47
1

There is also the possibility to use XML with LINQ:

Create XML tree in C# (LINQ to 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. The configuration of your tree also gets much cleaner and user-friendly as you can simply use an XML file for the initialization.

If you need to work with objects, you can use XML serialization:

XML serialization

1
  • This is a good opportunity to practice French, but perhaps provide the corresponding English ones as well? Dec 5, 2021 at 20:01
0

If you are going to display this tree on the GUI, you can use TreeView and TreeNode. (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.)

0

Here is my implementation of a 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();
    }
}
0

I don't like a tree aproach. It gets things overcomplicated including search or dril-down or even ui controls populating.

I would suggest to use a very simple approach with IDictionary<TChild, TParent>. This also allows to have no connections between nodes or levels.

-1

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

2
  • What unit test framework? NUnit? Dec 5, 2021 at 20:02
  • An explanation would be in order. E.g., what is the idea/gist? What is the purpose of SafeRelease()? E.g., why is SafeRelease() necessary? Thread safety? What is the thinking behind the decision to use async and await? What is the minimum version of C# required? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today). Dec 5, 2021 at 20:05
-3

In case you need a rooted tree data structure implementation that uses less memory, you can write your Node class as follows (C++ implementation):

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
}
3
  • 12
    Posting C++ code on a question specifically for C# is not the best idea, Jake. Especially one that includes pointers. You do know that pointers are being hunted down mercilessly in C#, right? :p
    – ThunderGr
    Oct 16, 2013 at 11:40
  • 2
    @ThunderGr that is not fair. Answering in C# would have been better, but those C++ pointers can be understood by C#-speakers as references (they're less safe, ok). After David Boike, Aaron Gage, Ronnie Overby, Grzegorz Dev, Berezh and Erik Nagel all having sugested basically the same data structure with minor differences only in expression, Jake sugested breaking down the linked list yielding a simpler structures with only one type of node and sibling navigability. Don't express your dislike of C++ by down-voting a constructive answer.
    – migle
    May 20, 2014 at 9:35
  • 3
    @migle I did not downvote the answer(did not upvote either). And I do not dislike C++. I saw that the answer was downvoted without anyone suggesting anything to Jake about why and how he would improve his answer. It is not about "being better". The question is tagged for C# only. Posting answers in another language than the tag is not recommended and some people will downvote.
    – ThunderGr
    May 20, 2014 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.