using System.Collections.Generic;
public int Id { get; set; }
public List<int> ChildrenIds { get; set; }
public Node(int id, List<int> childrenIds = null)
ChildrenIds = childrenIds ?? new List<int>();
public static class TreeExtensions
public static List<int> GetRelatedNodeIds(this Node root, int targetNodeId)
var targetNodePath = FindPathToNode(root, targetNodeId);
if (targetNodePath == null)
var relatedNodeIds = new HashSet<int>();
foreach(var nodeId in targetNodePath)
relatedNodeIds.Add(nodeId);
foreach (var ancestorId in targetNodePath)
var ancestor = FindNodeById(root, ancestorId);
relatedNodeIds.UnionWith(GetAllDescendantIds(ancestor));
return relatedNodeIds.ToList();
private static List<int> FindPathToNode(Node root, int targetNodeId)
if (root.Id == targetNodeId)
return new List<int> { root.Id };
foreach(var childId in root.ChildrenIds)
var child = FindNodeById(root, childId);
var path = FindPathToNode(child, targetNodeId);
private static Node FindNodeById(Node root, int nodeId)
foreach(var childId in root.ChildrenIds)
var child = FindNodeById(root, childId);
private static HashSet<int> GetAllDescendantIds(Node node)
var descendants = new HashSet<int>();
descendants.Add(node.Id);
foreach(var childId in node.ChildrenIds)
var child = FindNodeById(node, childId);
descendants.UnionWith(GetAllDescendantIds(child));
public void TestRelatedNodes_TargetIsLeafNode()
var root = new Node(1, new List<int> { 2, 3, 4 });
var node2 = new Node(2, new List<int>{5,6});
var node3 = new Node(3, new List<int>{7});
var nodes = new List<Node>() { root, node2, node3, node4, node5, node6, node7 };
var relatedNodeIds = root.GetRelatedNodeIds(5);
CollectionAssert.AreEquivalent(new List<int>{1,2,5,6}, relatedNodeIds);
public void TestRelatedNodes_TargetIsMiddleNode()
var root = new Node(1, new List<int> { 2, 3, 4 });
var node2 = new Node(2, new List<int>{5,6});
var node3 = new Node(3, new List<int>{7});
var nodes = new List<Node>() { root, node2, node3, node4, node5, node6, node7 };
var relatedNodeIds = root.GetRelatedNodeIds(3);
CollectionAssert.AreEquivalent(new List<int> { 1, 3, 7 }, relatedNodeIds);
public void TestRelatedNodes_TargetIsRootNode()
var root = new Node(1, new List<int> { 2, 3, 4 });
var node2 = new Node(2, new List<int>{5,6});
var node3 = new Node(3, new List<int>{7});
var nodes = new List<Node>() { root, node2, node3, node4, node5, node6, node7 };
var relatedNodeIds = root.GetRelatedNodeIds(1);
CollectionAssert.AreEquivalent(new List<int> { 1, 2, 3, 4, 5, 6, 7 }, relatedNodeIds);
public void TestRelatedNodes_TargetNodeDoesNotExist()
var root = new Node(1, new List<int> { 2, 3, 4 });
var node2 = new Node(2, new List<int>{5,6});
var node3 = new Node(3, new List<int>{7});
var nodes = new List<Node>() { root, node2, node3, node4, node5, node6, node7 };
var relatedNodeIds = root.GetRelatedNodeIds(9);
Assert.IsEmpty(relatedNodeIds);
public void TestRelatedNodes_EmptyTree()
var relatedNodeIds = ((Node)null).GetRelatedNodeIds(1);
Assert.IsEmpty(relatedNodeIds);
public void TestRelatedNodes_SingleNodeTree()
var relatedNodeIds = root.GetRelatedNodeIds(1);
CollectionAssert.AreEquivalent(new List<int> {1}, relatedNodeIds);
var relatedNodeIds_NotFound = root.GetRelatedNodeIds(2);
Assert.IsEmpty(relatedNodeIds_NotFound);
public void TestRelatedNodes_UnbalancedTree()
var root = new Node(1, new List<int>() {2});
var node2 = new Node(2, new List<int>() {3});
var node3 = new Node(3, new List<int>() {4});
var nodes = new List<Node>() { root, node2, node3, node4 };
var relatedNodeIds = root.GetRelatedNodeIds(3);
CollectionAssert.AreEquivalent(new List<int> {1, 2, 3, 4}, relatedNodeIds);
relatedNodeIds = root.GetRelatedNodeIds(4);
CollectionAssert.AreEquivalent(new List<int> {1,2,3,4}, relatedNodeIds);
relatedNodeIds = root.GetRelatedNodeIds(1);
CollectionAssert.AreEquivalent(new List<int> {1, 2, 3, 4}, relatedNodeIds);
public static void Main(string[] args)
Console.WriteLine("-------------- STARTING UNIT TESTING ---------------");
new NUnitLite.AutoRun().Execute(["--noc" ]);