Tree Data Structure in C#

Tree data structure in C#

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

Build a simple, high performance Tree Data Structure in c#

Just make a class out of it.

UPDATED:

class TreeNode : IEnumerable<TreeNode>
{
private readonly Dictionary<string, TreeNode> _children =
new Dictionary<string, TreeNode>();

public readonly string ID;
public TreeNode Parent { get; private set; }

public TreeNode(string id)
{
this.ID = id;
}

public TreeNode GetChild(string id)
{
return this._children[id];
}

public void Add(TreeNode item)
{
if (item.Parent != null)
{
item.Parent._children.Remove(item.ID);
}

item.Parent = this;
this._children.Add(item.ID, item);
}

public IEnumerator<TreeNode> GetEnumerator()
{
return this._children.Values.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}

public int Count
{
get { return this._children.Count; }
}
}

Usage will be fairly simple to statically define:

var tree = new TreeNode("Root")
{
new TreeNode("Category 1")
{
new TreeNode("Item 1"),
new TreeNode("Item 2"),
new TreeNode("Item 3"),
},
new TreeNode("Category 2")
{
new TreeNode("Item 1"),
new TreeNode("Item 2"),
new TreeNode("Item 3"),
new TreeNode("Item 4"),
}
};

Edit

Some more functionality for even easier creation...

public static TreeNode BuildTree(string tree)
{
var lines = tree.Split(new[] { Environment.NewLine },
StringSplitOptions.RemoveEmptyEntries);

var result = new TreeNode("TreeRoot");
var list = new List<TreeNode> { result };

foreach (var line in lines)
{
var trimmedLine = line.Trim();
var indent = line.Length - trimmedLine.Length;

var child = new TreeNode(trimmedLine);
list[indent].Add(child);

if (indent + 1 < list.Count)
{
list[indent + 1] = child;
}
else
{
list.Add(child);
}
}

return result;
}

public static string BuildString(TreeNode tree)
{
var sb = new StringBuilder();

BuildString(sb, tree, 0);

return sb.ToString();
}

private static void BuildString(StringBuilder sb, TreeNode node, int depth)
{
sb.AppendLine(node.ID.PadLeft(node.ID.Length + depth));

foreach (var child in node)
{
BuildString(sb, child, depth + 1);
}
}



Usage:

var tree = TreeNode.BuildTree(@"
Cat1
Sub1
Item1
Item2
Item3
Sub2
Item1
Item2
Cat2
Sub1
Sub2
Item1
Item2
Sub3
Item1
Cat3
Cat4");

Why is there no Tree T class in .NET?

You're right, there's nothing in the BCL. I suspect this is because the choice of whether to use a tree is typically an implementation detail and is otherwise an unconventional way to access data. That is, you don't say, "binary-search-for element #37"; instead, you say, "get me element #37".

But have you taken a look at C5? It's super-handy and they have several tree implementations (1, 2, 3).

Loading flat data into a tree data structure

First question: recursive loading

You need to create a helper function (if you code in C# 7.0 you can do that as a local function and strip the items parameter):

private static void AddDescendants(IReadOnlyCollection<Item> items, TreeNode<Item> node)
{
var children = node.AddChildren(items.Where(i => i.Parent == node.Value.Child).ToArray());
foreach (var child in children)
{
AddDescendants(items, child);
}
}

Calling it, as per your example after obtaining root:

var root = new TreeNode<Item>(items.Single(i => i.Parent == null));
AddDescendants(items, root);

Second question: Traversal

Your Traverse function does pre-order traversal and provides absolutely no information whatsoever on which tree level you are, and so cannot be used to perform indentation in output.

If you change the implementation to take Action<TreeNode<T>> like so:

public void Traverse(Action<TreeNode<T>> action)
{
action(this);
foreach (var child in _children)
child.Traverse(action);
}

Then you can calculate indentation level by counting parents:

root.Traverse(node =>
{
var depth = 0;
var ancestor = node.Parent;
while (ancestor != null)
{
depth++;
ancestor = ancestor.Parent;
}
Console.WriteLine(new string(' ', depth * 2) + node.Value.Name);
});

Here's a full working sample with output: https://ideone.com/faVOtd

What collection to store a tree structure?

Something like this can be a starting point. By using generics this one can hold a tree of anything

class TreeNode<T>
{
List<TreeNode<T>> Children = new List<TreeNode<T>>();

T Item {get;set;}

public TreeNode (T item)
{
Item = item;
}

public TreeNode<T> AddChild(T item)
{
TreeNode<T> nodeItem = new TreeNode<T>(item);
Children.Add(nodeItem);
return nodeItem;
}
}

A sample which holds a tree of strings

string root = "root";
TreeNode<string> myTreeRoot = new TreeNode<string>(root);
var first = myTreeRoot.AddChild("first child");
var second = myTreeRoot.AddChild("second child");
var grandChild = first.AddChild("first child's child");


Related Topics



Leave a reply



Submit