Skip to main content
1 of 9
Stacklysm
  • 223
  • 1
  • 2
  • 8

C# Recursive search on Node Tree with Linq and Queue

I've created a Node class which contains two important properties:

  • public Node Parent { get; private set; }
  • private List<Node> Children { get; set;}

As the name suggests, the Parent object holds information about the ancestor of a certain node, if the parent is the root of the tree, then the parent is set to null. And the Children collection stores all the descendant nodes.

The methods responsible for the searching are:

  • GetChildren()
  • GetChildrenRecursive()

All of them are described on the code documentation. But I am specially concerned with the performance and reliability of the searching algorithm and the tree structure implementation. And I'd like to hear some opinions on how I can improve the code quality.

  • Node.cs
/// <summary>
/// Represents a tree-like structure
/// </summary>
public class Node
{
    /// <summary>
    /// The ancestor (parent) of this node. Null if the current node is the root of the tree.
    /// </summary>
    public Node Parent { get; private set; }

    /// <summary>
    /// The descendats (children) of this node.
    /// </summary>
    private List<Node> Children { get; set; }

    /// <summary>
    /// Checks wheter the current node is the root of the tree.
    /// </summary>
    public bool IsRoot { get { return Parent != null; } }

    /// <summary>
    /// Checks wheter the current node has any children.
    /// </summary>
    public bool HasChildren { get { return Count > 0; } }

    /// <summary>
    /// The current node's children count.
    /// </summary>
    public int Count { get { return Children?.Count ?? 0; } }

    /// <summary>
    /// The object stored in the current node.
    /// </summary>
    public object Value { get; set; }

    /// <summary>
    /// Creates a new instance of the <see cref="Node"/> class with an empty object.
    /// </summary>
    /// <param name="value">The value that will be held by this node</param>
    public Node()
    {
        Value = new object();
        Children = new List<Node>();
    }

    /// <summary>
    /// Creates a new instance of the <see cref="Node"/> class with a set value.
    /// </summary>
    /// <param name="value">The value that will be held by this node</param>
    public Node(object value)
    {
        Value = value;
        Children = new List<Node>();
    }

    /// <summary>
    /// Returns a copy of all values contained in this <see cref="Node"/>.
    /// <para>
    /// Useful for avoiding interferences between instances of the <see cref="Node"/> class.
    /// </para>
    /// </summary>
    /// <returns>A <see cref="Node"/> with the property values of this node</returns>
    public Node DeepCopy()
    {
        var other = (Node)MemberwiseClone();

        other.Children = new List<Node>(collection: Children);
        other.Parent = Parent?.DeepCopy();
        other.Value = new Node(value: Value);

        return other;
    }

    /// <summary>
    /// Adds a child to this <see cref="Node"/>.
    /// </summary>
    /// <param name="node">The node to be added</param>
    public void AddChild(Node node)
    {
        if (node != this && node.Parent == null)
        {
            node.Parent = this;

            Children.Add(node);
        }
    }

    /// <summary>
    /// Removes a child from this <see cref="Node"/>.
    /// </summary>
    /// <param name="node">The node to be removed</param>
    public void RemoveChild(Node node)
    {
        if (node != this && Children.Contains(node))
        {
            Children.Remove(node);
        }
    }

    /// <summary>
    /// Performs a superficial search, returning the children on the first level.
    /// </summary>
    /// <returns>An <see cref="IEnumerable{Node}"/>containing the search result</returns>
    public IEnumerable<Node> GetChildren()
    {
        return Children.AsEnumerable();
    }

    /// <summary>
    /// Performs a recursive search, returning all the children on all levels
    /// </summary>
    /// <returns>An <see cref="IEnumerable{Node}"/>containing the search result</returns>
    public IEnumerable<Node> GetChildrenRecursive()
    {         
        var root = DeepCopy();

        // No descendants have children. No recursion neeeded.
        if (root.Children.All(x => x.Children.Count == 0))
        {
            return GetChildren();
        }
        // Some (or all) descendants have children. Use recursion
        else
        {
            var allChildren = new List<Node>();
            var searchQueue = new Queue<Node>();

            // Adds the first generation of children into the queue
            GetChildren().ToList()
                .ForEach((x) => searchQueue.Enqueue(x));

            // Loops until the queue is empty
            while (searchQueue.Any())
            {
                // Adds the first children in the queue to the final collection
                allChildren.Add(searchQueue.Peek());

                // Checks if the first children in the queue has descendants
                if (searchQueue.Peek().HasChildren)
                {
                    // Adds the descendants of the children being searched on the queue
                    searchQueue.Peek().Children
                        .ForEach((x) => searchQueue.Enqueue(x));
                }

                // Removes the first node on the queue, since it has been searched already.
                searchQueue.Dequeue();
            }

            return allChildren;
        }
    }

    /// <summary>
    /// Override for the <code><see cref="object"/>.ToString()</code> method
    /// </summary>
    /// <returns>The string representation of this node's value</returns>
    public override string ToString()
    {
        return $"{Value?.ToString()}";
    }
}
```
Stacklysm
  • 223
  • 1
  • 2
  • 8