C# Tabbed Text File to Tree - c#

I have text file a tabbed list that need to turned into a tree.
File content:
root
node1
node2
node2_1
node3
node3_1
node3_2
node3_2_1
node4
Tree should look like:
root
/ | \ \
node1 node2 node3 node4
| / \
node2_1 node3_1 node3_2
\
node3_2_1
I am trying to create a recursive method to create the tree but not sure how to keep track of parent and and root.

Assuming your Node class looks like this:
public class Node
{
public Node(string value)
{
Value = value;
Children = new List<Node>();
}
public string Value { get; }
public List<Node> Children{ get; }
}
You can try this method to get the tree (assuming your input is as you posted and the root is the first line):
public Node GetTree(string filePath)
{
string[] lines = System.IO.File.ReadAllLines(filePath);
//if lines is empty the tree is null
if(lines.Length == 0)
{
return null;
}
//create the root
Node root = new Node(lines[0].Trim());
//get the root leading white spaces length
int rootWhiteSpaceLength = lines[0].TakeWhile(c => c=" ").Count();
//Crete a list to hold the nodes and their white space length
List<Tuple<Node, int>> nodesAndLengths = new List<Tuple<Node, int>>();
//add the root to nodes list with it's white space length
nodesAndLengths.Add(new Tuple<Node, int>(root, rootWhiteSpaceLength));
//iterate over the lines strating from index 1
for (var i = 1; i < lines.Length, i++)
{
//create the node
Node node = new Node(lines[i].Trim());
//get the node leading white spaces length
int nodeWhiteSpaceLength = lines[i].TakeWhile(c => c=" ").Count();
//Check that is not another root
if(nodeWhiteSpaceLength <= rootWhiteSpaceLength)
{
throw new Exception("There is more than one root");
}
//find the node parent in the nodes list using LINQ
Node parent = nodesAndLengths.Last(nodeAndLength => nodeAndLength.Item2 < nodeWhiteSpaceLength).Item1;
//Alternatively using while loop
//int j = i - 1;
//while(nodeWhiteSpaceLength >= nodesAndLengths[j].Item2)
//{
//j--;
//}
//Node parent=nodesAndLengths[j].Item1;
//add the node to it's parent
parent.Children.Add(node);
//add the node to nodes list with it's white space length
nodesAndLengths.Add(New Tuple<Node, int>(node, nodeWhiteSpaceLength));
}
return root;
}

Related

How do I parse commands from a text file in C#?

I am creating an application for my C# class in school that reads commands in from text file and displays the results in the console. Basically there will be commands in the file that should add a node and delete a node from a singly linked list. So if in the input file it reads I:1, I:7 it would output Node1:1 and Node2:7 to the console.
I know how to parse a file to display things like hello world, etc. But I am unsure as to how I would parse a file to look for a specific string and then output results based on that string (in this case appending and deleting nodes from a list). Is there any resources on how one would do that?
This is what I have for my program so far:
public class LinkedList {
Node head; //the head of list
public class Node {
public int data;
public Node next;
//constructor
public Node(int d) {
data = d;
next = null;
} //end of constructor
}
public void printList() { //traversing list and printing the contents starting from head(1)
Node n = head;
while (n != null) {
Console.Write(n.data + " ");
n = n.next;
}
}
public void push(int new_data) {
Node new_node = new Node(new_data); //allocate new node, put in data
new_node.next = head; //make next of new node as head
head = new_node; //move the head to point to new node
}
//main method to create a linked list with 3 nodes
public static void Main(String[] args) {
//starting with an empty list
LinkedList llist = new LinkedList();
llist.head = new Node(1);
Node second = new Node(2);
Node third = new Node(3);
//now 3 nodes have been allocated. head, second, and third
llist.head.next = second; //linking the head (1st) with the second node. these are both now linked.
second.next = third; //linking second with third. now linked. all 3 are linked now
llist.printList();
}
} //end of class program

trouble with adding child node C#

I am adding a child node to the current parent node in treeview. But my problem is that it adds the new node to the end of the current parent rather than to add in the position which the if is true.
Here is my code:
for (int i = 0; i < num; i++)
{
if (action_type1 != action_type2)
{
TreeNode new_node = = treeView1.Nodes[0].Nodes[position];
string new_name = "";
new_node.Nodes.Add(new_name);
}
}
of course num, position, action_type1, and action_type2 are variables in my code and for any for loop they are different integers an strings. action_type1 is the name of nodes of treeView and action_type2 is a fixed string. if loop looks for whole the tree if there is nodes equal with the given string then leave the node otherwise insert an empty node in the tree and then do recursively.
but to make it simple, let we have:
int num = 2;
int position = 4;
string action_type1;
string action_type2;
This is what you want?
for (int i = 0; i < num; i++)
{
if (action_type1 != action_type2)
{
treeView1.Nodes[i].Nodes.Insert(position - 1, virtual_name);
}
}

Finding all possible paths from one node to another?

I was trying to find all possible paths, but I am having hard time keeping track of the paths that I have visited. Here is the code so far:
public void FindAllPaths(Node startNode, Node endNode)
{
queue.Enqueue(startNode);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
foreach (var edge in currentNode.Edges)
{
if (edge.Visisted)
continue;
edge.Visisted = true;
queue.Enqueue(edge.TargetNode);
}
}
}
You have to keep track of the paths visited for each route, not globally. For a breadth first approach each route needs a list of visited paths. For a depth first approach you can either keep a list of visited paths, or keep it global but unvisit the paths as you backtrack.
Getting the length of the path and the total weight will more or less do itself, once you keep track of the paths for each route.
With your current algorithm you would enqueue an item that has a node and a list of visited paths:
public void FindAllPaths(Node startNode, Node endNode) {
queue.Enqueue(new QueueItem(startNode, new List<Edge>()));
while (queue.Count > 0) {
var currentItem = queue.Dequeue();
foreach (var edge in currentItem.Node.Edges) {
if (!currentItem.Visited.Contains(edge)) {
List<Edge> visited = new List<Edge>(currentItem.Visited);
visited.Add(edge);
if (edge.TargetNode == endNode) {
// visited.Count is the path length
// visited.Sum(e => e.Weight) is the total weight
} else {
queue.Enqueue(new QueueItem(edge.TargetNode, visited));
}
}
}
}
}
The QueueItem class is just:
public class QueueItem {
public Node Node { get; private set; }
public List<Edge> Visited { get; private set; }
public QueueItem(Node node, List<Edge> visited) {
Node = node;
Visited = visited;
}
}
I set up the paths this way:
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
a.Edges.Add(new Edge(5, b));
a.Edges.Add(new Edge(7, e));
a.Edges.Add(new Edge(5, d));
b.Edges.Add(new Edge(4, c));
c.Edges.Add(new Edge(2, e));
c.Edges.Add(new Edge(8, d));
d.Edges.Add(new Edge(8, c));
d.Edges.Add(new Edge(6, e));
e.Edges.Add(new Edge(3, b));
If you go A-B-C-E then C will be marked as visited, but since C is also part of the path A-D-C-E you won't be able to find the later. Therefore a depth-first approach seems more appropriate, as it allows you to work on one path at a time. After you are finished with a path, you can clear the Visited-flags and continue with another path. I'm trying to scetch a solution in pseudo code:
declare path as list of node;
procedure FindPath(node)
for each edge in node.Edges
if not edge.Visited then
edge.Visited = true
path.Append(edge.TargetNode)
if edge.TargetNode = goal then
Print(path)
else
FindPath(edge.TargetNode)
end
path.Remove(edge.TargetNode)
edge.Visited = false
end
end
end
Where goal is node E in your example. You would call FindPath with the start node
FindPath(A);
As stated previously, maintaining a Visited property on each edge will not work because a given edge may be present in multiple distinct paths. For example, the D/E edge will be traversed for both the A->D->E path and the A->B->C->D->E path.
You need to maintain the current path for each node added to the queue:
IEnumerable<Path> FindAllPaths(Node from, Node to)
{
Queue<Tuple<Node, List<Node>>> nodes = new Queue<Tuple<Node, List<Node>>>();
nodes.Enqueue(new Tuple<Node, List<Node>>(from, new List<Node>()));
List<Path> paths = new List<Path>();
while(nodes.Any())
{
var current = nodes.Dequeue();
Node currentNode = current.Item1;
if (current.Item2.Contains(currentNode))
{
continue;
}
current.Item2.Add(currentNode);
if (currentNode == to)
{
paths.Add(new Path(current.Item2));
continue;
}
foreach(var edge in current.Item1.Edges)
{
nodes.Enqueue(new Tuple<Node, List<Node>>(edge.Target, new List<Node>(current.Item2)));
}
}
return paths;
}

How to get to next list in the List<List<Node>> when particular condition is satisfied

I have a List<Node> nodes and each node in nodes have a variable called Interest associated with it.
public static List<Node> nodes = new List<Node>();
for (int i = 0; i < Program.n; i++)
{
//int densityrandom = generateRandom();
Node n = new Node(Interest3);
Program.nodes.Add(n);
}
While class Node is as Follows:
public class Node
{
public int Density { get; set; }
public InterestProfile P1 { get; set; }
public List<Edge> adjnodes { get; set; }
public Node(InterestProfile a)
{
adjnodes = new List<Edge>();
P1 = a;
}
}
For some object Interest1 of type InterestProfile there is an array associated with it like
Interest1 = new InterestProfile(array1);
So, if you do something like nodes[0].P1.InterestArray[0], it will give me first element in the array1.
Now here is my actual question:
I am splitting up the list nodes according to user input into list of list of Node. Thus, if nodes have 100 elements in it, it will be split up into 10 lists and all those lists will stored in one list.
This is something I have in mind:
List<List<Node>> smallList = new List<List<Node>>();
for (int i = 0; i < nodes.Count; i++)
{
for(int k=0; k < smallList.Count; k++)
{
if (list[i].P1.InterestArray[0] > 5 && smallList[k].Count != 10)
{
smallList[0].Add(list[i]);
}
else smallList[k+1].Add(list[i]);
}
and so on by checking for all 5 elements in an array associated with particular node. However, when smallList[0] reaches limit of 10, I want to start adding elements from nodes in some other smallList[i]. When all the values of particular InterestArray[j] are checked for all nodes in nodes, then only it should move to checking InterestArray[j+1]. How can I program this in c#? Is my implementation correct and if yes can it be improved?
I know question is quite confusing. Please ask me if anything is difficult to understand. Any help will be highly appreciated.
Instead of creating the List<List<Node>> data structure why not simply query your existing List<Node> for the nodes you want when you need them?
e.g.
Old School:
var interestingNodes = new List<Node>();
foreach(Node node in nodes)
{
foreach(int i in note.InterestArray)
{
if(i > 5)
interestingNodes.Add(node);
}
}
Using List<T>.FindAll():
var interstingNodes = nodes.FindAll(node => node.P1.InterestArray.Contains(5));
Or in LINQ:
var interstingNodes = from node in nodes
where node.P1.InterestArray.Contains(5)
select node;

Height of binary search tree iteratively

I was trying out an iterative method to find the height/depth of a binary search tree.
Basically, I tried using Breadth First Search to calculate the depth, by using a Queue to store the tree nodes and using just an integer to hold the current depth of the tree. Each node in the tree is queued, and it is checked for child nodes. If child nodes are present, then the depth variable is incremented. Here is the code:
public void calcDepthIterative() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
TreeNode node = root;
int level = 0;
boolean flag = false;
nodeQ.add(node);
while(!nodeQ.isEmpty()) {
node = nodeQ.remove();
flag = false;
if(node.leftChild != null) {
nodeQ.add(node.leftChild);
flag = true;
}
if(node.rightChild != null) {
nodeQ.add(node.rightChild);
flag = true;
}
if(flag) level++;
}
System.out.println(level);
}
However, the code doesn't work for all cases. For example, for the following tree:
10
/ \
4 18
\ / \
5 17 19
It shows the depth as 3, instead of 2.
I did an alternate version of it using an additional Queue to store the current depths, using the idea in this page. I wanted to avoid using an additional queue so I tried to optimize it. Here is the code which works, albeit using an additional Queue.
public void calcDepthIterativeQueue() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
Queue<Integer> lenQ = new LinkedList<Integer>();
TreeNode node = root;
nodeQ.add(node);
lenQ.add(0);
int maxLen = 0;
while(!nodeQ.isEmpty()) {
TreeNode curr = nodeQ.remove();
int currLen = lenQ.remove();
if(curr.leftChild != null) {
nodeQ.add(curr.leftChild);
lenQ.add(currLen + 1);
}
if(curr.rightChild != null) {
nodeQ.add(curr.rightChild);
lenQ.add(currLen + 1);
}
maxLen = currLen > maxLen ? currLen : maxLen;
}
System.out.println(maxLen);
}
QUESTION:
Is there a way to fix the first method such that it returns the right depth?
EDIT
SEE ACCEPTED ANSWER BELOW
Java code for rici's answer:
public void calcDepthIterative() {
Queue<TreeNode> nodeQ = new LinkedList<TreeNode>();
int depth = 0;
nodeQ.add(root);
while(!nodeQ.isEmpty()) {
int nodeCount = nodeQ.size();
if(nodeCount == 0)
break;
depth++;
while(nodeCount > 0) {
TreeNode topNode = nodeQ.remove();
if(topNode.leftChild != null)
nodeQ.add(topNode.leftChild);
if(topNode.rightChild != null)
nodeQ.add(topNode.rightChild);
nodeCount--;
}
}
System.out.println(depth);
}
Here's one way of doing it:
Create a Queue, and push the root onto it.
Let Depth = 0
Loop:
Let NodeCount = size(Queue)
If NodeCount is 0:
return Depth.
Increment Depth.
While NodeCount > 0:
Remove the node at the front of the queue.
Push its children, if any, on the back of the queue
Decrement NodeCount.
How it works
Every time NodeCount is set, the scan is just about to start a new row. NodeCount is set to the number of Nodes in that row. When all of those Nodes have been removed (i.e., NodeCount is decremented to zero), then the row has been completed and all the children of nodes on that row have been added to the queue, so the queue once again has a complete row, and NodeCount is again set to the number of Nodes in that row.
public int height(Node root){
int ht =0;
if(root==null) return ht;
Queue<Node> q = new ArrayDeque<Node>();
q.addLast(root);
while(true){
int nodeCount = q.size();
if(nodeCount==0) return ht;
ht++;
while(nodeCount>0){
Node node = q.pop();
if(node.left!=null) q.addLast(node.left);
if(node.right!=null) q.addLast(node.right);
nodeCount--;
}
}
How about recurtion,
int Depth(Node node)
{
int depthR=0,depthL=0;
if(Right!=null)depthR=Depth(Right);
if(Left!=null)depthL=Depth(Left);
return Max(depthR,depthL)+1;
}
If tou want a zero based depth, just subtract the resulting depth by 1.

Categories

Resources