I need some help implementing Dijkstra's Algorithm and was hoping someone would be able to assist me. I have it so that it is printing some of routes but it isn't capturing the correct costs for the path.
Here is my node structure:
class Node
{
public enum Color {White, Gray, Black};
public string Name { get; set; } //city
public List<NeighborNode> Neighbors { get; set; } //Connected Edges
public Color nodeColor = Color.White;
public int timeDiscover { get; set; }//discover time
public int timeFinish { get; set; } // finish time
public Node()
{
Neighbors = new List<NeighborNode>();
}
public Node(string n, int discover)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
timeDiscover = discover;
}
public Node(string n, NeighborNode e, decimal m)
{
Neighbors = new List<NeighborNode>();
this.Name = n;
this.Neighbors.Add(e);
}
}
class NeighborNode
{
public Node Name { get; set; }
public decimal Miles { get; set; } //Track the miles on the neighbor node
public NeighborNode() { }
public NeighborNode(Node n, decimal m)
{
Name = n;
Miles = m;
}
}
Here is my algorithm:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
Stack<Node> _allCities = new Stack<Node>(); // add all cities into this for examination
Node _nodeToExamine = new Node(); //this is the node we're currently looking at.
decimal _cost = 0;
foreach (var city in graph) // putting these onto a stack for easy manipulation. Probably could have just made this a stack to start
{
_allCities.Push(city);
_algorithmList.Add(new DA(city));
}
_nodeToExamine = _allCities.Pop(); //pop off the first node
while (_allCities.Count != 0) // loop through each city
{
foreach (var neighbor in _nodeToExamine.Neighbors) //loop through each neighbor of the node
{
for (int i = 0; i < _algorithmList.Count; i++) //search the alorithm list for the current neighbor node
{
if (_algorithmList[i].Name.Name == neighbor.Name.Name) //found it
{
for (int j = 0; j < _algorithmList.Count; j++) //check for the cost of the parent node
{
if (_algorithmList[j].Name.Name == _nodeToExamine.Name) //looping through
{
if (_algorithmList[j].Cost != 100000000) //not infinity
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
break;
}
}
_cost = _cost + neighbor.Miles;
if (_algorithmList[i].Cost > _cost) // check to make sure the miles are less (better path)
{
_algorithmList[i].Parent = _nodeToExamine; //set the parent to be the top node
_algorithmList[i].Cost = _cost; // set the weight to be correct
break;
}
}
}
}
_cost = 0;
_nodeToExamine = _allCities.Pop();
}
}
This is what the graph looks like:
The graph list node is essentially
Node -- Neighbor Nodes
So for example:
Node = Olympia, Neighbor Nodes = Lacey and Tacoma
I think the problem is that
_cost = _algorithmList[j].Cost; //set the cost to be the parent cost
You do a direct assignment of cost, instead of an addition of old and new cost.
Also, the fact that you do
if (_algorithmList[j].Cost != 100000000) //not infinity
directly before it means that if the cost of the path is infinity, you do the very opposite - you add zero to the cost of the path, making it the least expensive instead of most expensive path.
If you want to check for infinity properly, you have to outright skip taking that path when you inspect its cost, not just skip calculating the cost.
I needed to rewrite the entire algorithm as it wasn't processing correctly:
public void DijkstraAlgorithm(List<Node> graph)
{
List<DA> _algorithmList = new List<DA>(); //track the node cost/positioning
DA _nodeToExamine = new DA(); //this is the node we're currently looking at.
bool flag = true; //for exting the while loop later
foreach (var node in graph)
{
_algorithmList.Add(new DA(node));
}
foreach (var children in _algorithmList[0].Name.Neighbors) //just starting at the first node
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
_algorithmList[i].Parent = _algorithmList[0].Name;
_algorithmList[i].Cost = children.Miles;
_algorithmList[0].Complete = true;
}
}
}
while (flag) //loop through the rest to organize
{
_algorithmList = _algorithmList.OrderBy(x => x.Cost).ToList(); //sort by shortest path
for (int i = 0; i < _algorithmList.Count; i++) //loop through each looking for a node that isn't complete
{
if (_algorithmList[i].Complete == false)
{
_nodeToExamine = _algorithmList[i];
break;
}
if (i == 13) //if the counter reaches 13 then we have completed all nodes and should bail out of the loop
flag = false;
}
if (_nodeToExamine.Name.Neighbors.Count == 0) //set any nodes that do not have children to be complete
{
_nodeToExamine.Complete = true;
}
foreach (var children in _nodeToExamine.Name.Neighbors) //loop through the children/neighbors to see if there's one with a shorter path
{
for (int i = 0; i < _algorithmList.Count; i++)
{
if (children.Name == _algorithmList[i].Name)
{
if (_nodeToExamine.Cost + children.Miles < _algorithmList[i].Cost) //found a better path
{
_algorithmList[i].Parent = _nodeToExamine.Name;
_algorithmList[i].Cost = _nodeToExamine.Cost + children.Miles;
}
}
}
_nodeToExamine.Complete = true;
}
}
PrintDijkstraAlgoirthm(_algorithmList);
}
public void PrintDijkstraAlgoirthm(List<DA> _finalList)
{
foreach (var item in _finalList)
{
if (item.Parent != null)
Console.WriteLine("{0} ---> {1}: {2}", item.Parent.Name, item.Name.Name, item.Cost);
}
}
Related
I’ve written an implementation of A* that relies on sorting nodes by their F score in a sortedSet.
The sorting, in some cases, seems to insert a Node object at the 'Min' value when its compared 'F' value is actually the second lowest rather than the Min, as described. I'm completely baffled as to why this is happening. I believe it's causing the knock-on effect of causing nodeTree.Remove and nodeTree.RemoveWhere to fail, but that might be the actual cause of the issue, I'm honestly not sure - though I wouldn't know how to fix it if it is.
This is the comparer used. I assume it's relatively obvious that I'm not exactly sure how to implement these, but I think this should work as I intend.
public class FValueFirst : Comparer<PathfindingAgent.Node>
{
public override int Compare(PathfindingAgent.Node x, PathfindingAgent.Node y)
{
int result = x.F.CompareTo(y.F);
if (result == 0)
{
result = y.G.CompareTo(x.G);
}
if(x == y)
{
result = 0;
}
return result;
}
}
This is the Node object, for reference.
public class Node
{
public Cell cell;
public float G;
public float H;
public bool Opened;
public bool Closed;
public Node Previous;
public float F { get => G + H; }
}
This is the function it all occurs in. The result is deterministic, thankfully. Depending on the current destID and the particular layout of the grid's obstacles it will always get out of sort on the same iteration.
public void PathTo(Vector3Int destID)
{
SortedSet<Node> nodeTree = new SortedSet<Node>(new FValueFirst());
Vector3Int radius = PathfindingGrid.Instance.GridRadius;
NodeGrid = new Node[radius.x * 2 + 1, radius.y * 2 + 1, radius.z * 2 + 1];
Node startNode = new Node()
{
cell = PathfindingGrid.Cells[CurrentID.x, CurrentID.y, CurrentID.z],
G = 0,
H = 0
};
Node endNode = new Node()
{
cell = PathfindingGrid.Cells[destID.x, destID.y, destID.z],
G = 0,
H = 0
};
Vector3Int sID = startNode.cell.ID;
Vector3Int eID = endNode.cell.ID;
NodeGrid[sID.x, sID.y, sID.z] = startNode;
NodeGrid[eID.x, eID.y, eID.z] = endNode;
if (endNode.cell.IsOccupied) return;
nodeTree.Add(startNode);
int iterations = 0;
while(true)
{
Node node;
node = nodeTree.Min;
node.Closed = true;
nodeTree.RemoveWhere(n => n == node);
if(node == nodeTree.Min)
{
throw new Exception($"Incorrect node was removed from the tree");
}
if (node == endNode)
{
List<Node> chain = BacktraceChain(node);
Debug.Log($"Path found from {CurrentID} to {destID} with score {endNode.G} traversing {chain.Count} cells in {iterations} iterations");
DrawLine(chain, Color.white);
break;
}
List<Node> neighbours = GetNeighbours(node);
foreach(Node neighbour in neighbours)
{
if (neighbour == startNode || neighbour.Closed) continue;
float newg = Vector3Int.Distance(node.cell.ID, neighbour.cell.ID) + node.G;
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
}
}
iterations++;
}
}
For posterity, I solved the issue - it was due to my inexperience with the SortedList type.
This code, found near the end of the function was to blame
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
Specifically, an item in a tree cannot have its compared values modified to the point where it no longer compares correctly in that index. The item must first be removed from the list, modified, and readded.
My guess in hindsight is that, though removed immediately after modification, the tree is unable to be sufficiently traversed to access the target item due to the modification.
Thus my solution was to simply re-arrange the block so that the removal and addition occured on either side of the modification respectively, like so:
if (!neighbour.Opened || newg < neighbour.G)
{
if (neighbour.Opened)
{
if (!nodeTree.Remove(neighbour)) throw new Exception($"{neighbour} was not removed from tree");
}
else
{
neighbour.Opened = true;
}
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
nodeTree.Add(neighbour);
}
I have a program which builds a very large tree from input data and traverses it, both by recursion. I have tested the program on smaller inputs (and thus smaller trees) and it functions as intended. However when the input data is much larger i run into 'Process is terminated due to StackOverflowException'. I assume this is due to the stack running out of space. Is there any way to prevent this or do I have to switch to building the tree via iteration instead? Or perhaps I am missing a case of infinite recursion somewhere?
Here is the code:
class Program
{
static int[] tileColors;
static Color[] colors;
static int totalTiles;
static void Main(string[] args)
{
Stopwatch s = new Stopwatch();
s.Start();
string[] data = File.ReadAllLines("colors.txt");
totalTiles = int.Parse(data[0].Split(' ')[0]);
int totalColors = int.Parse(data[0].Split(' ')[1]);
string[] colorsRaw = data[1].Split(' ');
tileColors = new int[totalTiles];
for (int i = 0; i < totalTiles; i++)
{
tileColors[i] = int.Parse(colorsRaw[i]) - 1;
}
colors = new Color[totalColors];
for (int i = 3; i < data.Length; i++)
{
string[] raw = data[i].Split(' ');
int[] pair = new int[] { int.Parse(raw[0]) - 1, int.Parse(raw[1]) - 1 };
if (colors[pair[0]] == null)
colors[pair[0]] = new Color(pair[1]);
else
colors[pair[0]].pairs.Add(pair[1]);
if (colors[pair[1]] == null)
colors[pair[1]] = new Color(pair[0]);
else
colors[pair[1]].pairs.Add(pair[0]);
}
Tree t = new Tree();
t.root = new Node(0);
PopulateTree(t.root);
long ans = t.CountMatchingLeaves(t.root, totalTiles - 1) % 1000000007;
Console.WriteLine(ans);
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);
}
static void PopulateTree(Node root)
{
for (int i = root.tile + 1; i < totalTiles; i++)
{
if (colors[tileColors[i]] == null) continue;
if (colors[tileColors[i]].Compatible(tileColors[root.tile]))
{
var node = new Node(i);
root.children.Add(node);
PopulateTree(node);
}
}
}
}
class Color
{
public List<int> pairs = new List<int>();
public Color(int pair)
{
pairs.Add(pair);
}
public bool Compatible(int c)
{
return pairs.Contains(c);
}
}
class Node
{
public List<Node> children = new List<Node>();
public int tile;
public Node(int tile)
{
this.tile = tile;
}
}
class Tree
{
public Node root;
public List<Node> GetMatchingLeaves(Node root, int match)
{
if (root.children.Count == 0)
{
if (root.tile == match)
{
return new List<Node>() { root };
}
return new List<Node>();
}
List<Node> list = new List<Node>();
foreach(var c in root.children)
{
list.AddRange(GetMatchingLeaves(c, match));
}
return list;
}
public long CountMatchingLeaves(Node root, int match)
{
if (root.children.Count == 0)
{
if (root.tile == match)
{
return 1;
}
return 0;
}
long count = 0;
foreach (var c in root.children)
{
count += CountMatchingLeaves(c, match);
}
return count;
}
}
You can always rewrite recursion as iteration, usually by using a stack class rather than rely on your thread's stack. For your code it would look like this:
static void PopulateTree(Node start)
{
var nodes = new Stack<Node>();
nodes.Push(start);
while(nodes.Count != 0)
{
var root = nodes.Pop();
for (int i = root.tile + 1; i < totalTiles; i++)
{
if (colors[tileColors[i]] == null) continue;
if (colors[tileColors[i]].Compatible(tileColors[root.tile]))
{
var node = new Node(i);
root.children.Add(node);
nodes.Push(node);
}
}
}
}
The while loop checking for more items is the equivalent of your terminating condition in recursion.
in a rectangular array, I need to obtain a path consists of a series of ones starting from a given starting point (cell) in which to be the longest. I made a simple search of what are the suitable search algorithms to implement such a task and found that the prime algorithm is the most preferable technique.
Is it actually "Prime Algorithm" or other?
I tried using standard recursive based on Dijkstra's algorithm but I could not get the correct path.
The following code is for the windows form application, some times it output a correct results and sometimes not:
namespace AUV_Topology
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
/* Class Cell */
/*****************************************************************************************************************************/
public class Cell
{
public int row { get; set; }
public int col { get; set; }
public int value { get; set; }
public Boolean visited { get; set; }
}
/* Class EnumCell */
/*****************************************************************************************************************************/
public class EnumCell : IEnumerator<Cell>
{
public EnumCell() { }
public EnumCell(int startRow, int startCol, List<List<Cell>> graph)
{
this.row = startRow;
this.col = startCol;
this.numberOfRows = graph.Count;
this.numberOfCols = graph[0].Count;
this.graph = graph;
}
public enum XY
{
Y = 0, //row
X = 1 //col
}
public enum LOCATIONS : byte
{
TOP_LEFT = 0,
TOP_MIDDLE,
TOP_RIGHT,
LEFT,
RIGHT,
BOTTOM_LEFT,
BOTTOM_MIDDLE,
BOTTOM_RIGHT,
END,
INVALID
}
public List<List<Cell>> graph { get; set; }
public int row { get; set; }
public int col { get; set; }
public int numberOfRows { get; set; }
public int numberOfCols { get; set; }
//offsets are in same order as enum location as y-offset(row), x-offset (col)
private List<List<int>> offsets = new List<List<int>>() {
new List<int>() { -1, -1 },
new List<int>() { -1, 0 },
new List<int>() { -1, +1 },
new List<int>() { 0, -1 },
new List<int>() { 0, +1 },
new List<int>() { +1, -1 },
new List<int>() { +1, 0 },
new List<int>() { +1, +1 }
};
public LOCATIONS position { get; set; }
public EnumCell GetEnumerator()
{
return new EnumCell(row, col, graph);
}
object IEnumerator.Current
{
get {
return Current;
}
}
/* Class Current Cell */
/*****************************************************************************************************************************/
public Cell Current
{
get {
try {
// move to first valie postion
for (LOCATIONS location = position; location < LOCATIONS.END; location++) {
if ((row + offsets[(byte)location][(int)XY.Y] >= 0) && (row + offsets[(byte)location][(int)XY.Y] < numberOfRows) &&
(col + offsets[(byte)location][(int)XY.X] > 0) && (col + offsets[(byte)location][(int)XY.X] < numberOfCols)) {
position = (LOCATIONS)location;
int newRow = row + offsets[(byte)location][(int)XY.Y];
int newCol = col + offsets[(byte)location][(int)XY.X];
return graph[newRow][newCol];
}
}
throw new InvalidOperationException();
} catch (IndexOutOfRangeException) {
throw new InvalidOperationException();
}
}
}
public Boolean MoveNext()
{
Boolean results = false;
for (LOCATIONS location = ++position; location < LOCATIONS.END; location++) {
int y = offsets[(byte)location][(int)XY.Y];
int x = offsets[(byte)location][(int)XY.X];
if ((row + y >= 0) && (row + y < numberOfRows) &&
(col + x > 0) && (col + x < numberOfCols)) {
if (graph[row + y][col + x].value == 1) {
position = (LOCATIONS)location;
return true;
}
}
}
return results;
}
public void Reset()
{
position = LOCATIONS.TOP_LEFT;
}
public void Dispose()
{
}
}
/* Class Graph */
/*****************************************************************************************************************************/
public class Graph
{
public Graph(int[,] graph)
{
this.graph = new List<List<Cell>>();
for (int row = 0; row < graph.GetLength(0); row++) {
List<Cell> newRow = new List<Cell>();
this.graph.Add(newRow);
for (int col = 0; col < graph.GetLength(1); col++) {
Cell newCell = new Cell();
newRow.Add(newCell);
newCell.row = row;
newCell.col = col;
newCell.value = graph[row, col];
newCell.visited = false;
}
}
}
public List<List<Cell>> graph;
}
/* Class SpanningTree */
/*****************************************************************************************************************************/
class SpanningTree
{
public static Graph graph = null;
public static SpanningTree root = new SpanningTree();
public static int counter = 0;
public int row { get; set; }
public int col { get; set; }
public int length { get; set; }
public List<SpanningTree> children { get; set; }
public SpanningTree() { }
public SpanningTree(int startRow, int startCol, int[,] graph)
{
SpanningTree.graph = new Graph(graph);
RecursiveTree(root, SpanningTree.graph.graph[startRow][startCol]);
}
public int RecursiveTree(SpanningTree parent, Cell currentCell)
{
int length = 0;
int maxLength = 0;
parent.row = currentCell.row;
parent.col = currentCell.col;
graph.graph[currentCell.row][currentCell.col].visited = true;
EnumCell enumCell = new EnumCell(currentCell.row, currentCell.col, graph.graph);
foreach (Cell cell in enumCell) {
if (!cell.visited) {
SpanningTree newBranch = new SpanningTree();
if (parent.children == null) parent.children = new List<SpanningTree>();
parent.children.Add(newBranch);
length = RecursiveTree(newBranch, SpanningTree.graph.graph[cell.row][cell.col]);
if (length > maxLength) maxLength = length;
}
}
graph.graph[currentCell.row][currentCell.col].visited = false;
parent.length = maxLength;
return maxLength + 1;
}
public static void OrderHighLow(SpanningTree parent, int level)
{
if (parent.children != null) {
parent.children = parent.children.OrderByDescending(x => x.length).ToList();
foreach (SpanningTree child in parent.children) {
OrderHighLow(child, level + 1);
}
}
}
public static void Print(SpanningTree parent, int level, int chromosomeNum)
{
FileStream fs = new FileStream("C:/Users/Welcome/Desktop/TreeParser.txt", FileMode.Append, FileAccess.Write);
using (StreamWriter sw = new StreamWriter(fs)) {
sw.WriteLine("------------------- Chromosome : {0} -------------------", chromosomeNum);
Print(parent, level, sw);
sw.WriteLine("---------Longest----------");
PrintLongest(parent, level, sw);
counter = 0;
}
}
private static void Print(SpanningTree parent, int level, StreamWriter sw)
{
//////////////////////////////////////////////////////////////////////
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
//////////////////////////////////////////////////////////////////////
if (parent.children != null) {
foreach (SpanningTree child in parent.children) {
Print(child, level + 1, sw);
if (child.length == 0) {
sw.WriteLine("||,,,,,,Branch {0},,,,,,||", counter);
level = 0;
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, root.row, root.col, root.length);
counter += 1;
}
}
}
}
public static void PrintLongest(SpanningTree parent, int level, StreamWriter sw)
{
//////////////////////////////////////////////////////////////////////
sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
//////////////////////////////////////////////////////////////////////
if (parent.children != null) {
PrintLongest(parent.children[0], level + 1, sw);
}
}
}
}// end name space
I made call in the main form:
int tt = 0;
foreach (int[,] graph in rectArrays)
{
new SpanningTree(indexesX[tt], indexesY[tt], graph);
SpanningTree.OrderHighLow(SpanningTree.root, 0);
SpanningTree.Print(SpanningTree.root, 0,tt);
tt++;
}
Here is the latest results : Debug
Problem 1
Sometimes the path not as long as expected i.e. some cases (cells) not included while it should be!, sometimes the path contains cells with value zero, and sometimes it move from a cell to another cell while they are not adjacent to each other!.
Problem 2
When I use a big matrix such as 9 x 9 or above; I got the following:
Exception of type 'System.OutOfMemoryException' was thrown.
In method RecursiveTree replace the condition
if (!cell.visited)
by
if (!cell.visited && cell.value == 1)
This restricts the search to cells with value = 1 as required.
Your algorithm is rather complicated and entangled. Also, the naming makes it difficult to guess what things do. For instance, it is not clear what EnumCell enumerates. The name suggests that it is a kind of universal enumerator; however, MoveNext() returns only positions with value == 1. Why is there a variable results which is always false? Why is the logic split between MoveNext() and Current? Current should only return the value found in MoveNext. If MoveNext works correctly, no other logic should be required in Current. There are a lot of other such oddities in your code. C# has a yield return statement which makes writing an enumerator much easier.
Try to simplify your code and debug it.
I implemented a backtracking solution that returns all solutions with the maximum path length. Except for setup and printing the result, there is no other logic involved:
Declarations:
private int[,] _world;
private bool[,] _visited;
[DebuggerDisplay("Coord({Row}, {Col})")]
private struct Coord
{
public Coord(int row, int col)
{
this.Row = row;
this.Col = col;
}
public readonly int Row;
public readonly int Col;
}
Backtracking algorithm (call Find on a valid starting point of a path):
// Returns a list of paths of maximum length consisting of lists of coordinates.
private List<List<Coord>> Find(int row, int col)
{
_visited[row, col] = true;
var allResults = new List<List<Coord>>();
for (int i = Math.Max(0, row-1); i <= Math.Min(_world.GetLength(0)-1, row+1); i++) {
for (int j = Math.Max(0, col-1); j <= Math.Min(_world.GetLength(1)-1, col+1); j++) {
if (!_visited[i, j] && _world[i, j] == 1) {
List<List<Coord>> result = Find(i, j);
allResults.AddRange(result);
}
}
}
if (allResults.Count == 0) {
// This is an end-point of a path. Create the new path with current coord.
// We construct the paths backward.
allResults.Add(new List<Coord> { new Coord(row, col) });
} else {
// Keep only longest results
int maxLength = allResults.Max(p => p.Count);
for (int i = allResults.Count - 1; i >= 0; i--) {
if (allResults[i].Count < maxLength) {
allResults.RemoveAt(i);
} else {
// Prepend current point to path.
allResults[i].Insert(0, new Coord(row, col));
}
}
}
_visited[row, col] = false;
return allResults;
}
Note that the full result tree exists only on the call stack, but not as an explicit data structure. The paths (coordinate lists) are created from the path end-points backwards to the starting point.
With this world
private int[,] _world = new[,] {
{ 0, 1, 0, 1 },
{ 0, 1, 0, 1 },
{ 0, 1, 1, 0 },
{ 1, 0, 0, 1 },
{ 1, 1, 0, 1 },
};
For the starting coordinates (row = 0, column = 1) my solution prints (the printing routine itself is not shown):
Result #1, Length = 7
| ||1|| ||·|
| ||2|| ||·|
| ||4||3|| |
|5|| || ||·|
|6||7|| ||·|
Result #2, Length = 7
| ||1|| ||·|
| ||2|| ||·|
| ||4||3|| |
|5|| || ||·|
|7||6|| ||·|
If you comment out the section discarding shorter results, you can see that there are 8 possible paths (2 of length 5, 4 of length 6 and 2 of length 7).
I am implementing an N-1ry tree in C#. I am wondering how can I calculate the complexity of below methods. Here is my code:
Structure:
public class Node
{
public int Value { get; set; }
public Node Children { get; set; }
public Node Sibilings { get; set; }
}
This method for searching:
public Node search(Node root, int data)
{
if (root == null)
return null;
if (data == root.Value)
return root;
Node t = search(root.Children, data);
if (t == null)
t = search(root.Sibilings, data);
return t;
}
This method for insertion:
public void Add(int[] data)
{
Node temp = null;
temp = search(ROOT, data[0]);
if (temp == null)
temp = new Node(data[0]);
if (this.ROOT == null)
ROOT = temp;
Node parent = temp;
for (int j = 1; j <= this.NoOfChildrens; j++)
{
// for first child
if (j == 1)
{
parent.Children = new Node(data[j]);
parent = parent.Children;
}
//for all other childs
else
{
parent.Sibilings = new Node(data[j]);
parent = parent.Sibilings;
}
}
}
Program entry point:
static void Main(string[] args)
{
NAryTree naryTree = new NAryTree(3);
// 1st element in each row is node Value,>=2nd....=>value of child
int[][] data = { new int[] { 1, 2, 3, 4 }, new int[] { 2, 1, 6,0 }, new int[] { 3, 8, 9, 10 }, new int[] { 4, 0, 0, 0 } };
naryTree.Add(data[0]);
naryTree.Add(data[1]);
naryTree.Add(data[2]);
naryTree.Add(data[3]);
naryTree.Add(new int[] {10,3,6,4});
naryTree.preorder(naryTree.ROOT);
Console.ReadLine();
}
What is the bigO complexity of these methods?
Let's see what we have in Search method. It is not a binary tree and we have recursion. So the Search method will call N times till we find a necessary value. So we can conclude that we have O(N) where N is the maximum(worst) number of iteration to find a value at the last iteration:
public Node search(Node root, int data)
{
if (root == null)
return null;
if (data == root.Value)
return root;
Node t = search(root.Children, data);
if (t == null)
t = search(root.Sibilings, data);
return t;
}
For Addition method is simpler as we have for statement and no nested loops. So we have O(N) for Addition method.
As Wisconsin university says:
So for loops for (i = 0; i < N; i++) {
sequence of statements } The loop executes N times, so the sequence of statements also executes N times. Since we assume the
statements are O(1), the total time for the for loop is N * O(1),
which is O(N) overall.
I have a DataTable that has the following structure:
Root | Level 1 | Level 2 | Level 3 | Tree L | Tree R
Food 1 18
Fruit 2 11
Red 3 6
Cherry 4 5
Yellow 7 10
Banana 8 9
Meat 12 17
Beef 13 14
Pork 15 16
Using C#, I need to traverse this structure and calculate the correct Tree L and Tree R values for each node. This is just an example, the real structure has several hundred nodes that go out to at least Level 7, but possibly more.
Can anyone suggest how I might approach the code for calculating the left and right values?
So, you want to keep track of the visit order of the tree, but you have the order split into L and R. Those correspond to the "entrance" and "exit" of the Visit function for the Node structure. Assuming a Node class with a Node.Visit() method, you could:
private static List<Tuple<char,Node>> VisitOrder = new List<Tuple<char,Node>>();
public void Visit()
{
VisitOrder.Add(Tuple.Create('L', this));
// whatever visit logic you use here
VisitOrder.Add(Tuple.Create('R', this));
}
When you finish, all you have to do is do is look at the VisitOrder values. They're stored in the List in increasing order, where the index corresponds to it's position in the visit sequence. Each item in the List, then, is a Tuple describing which value it corresponds to and which Node it visited.
Edit:
To get the final output format, you could then do something like:
var LTree = VisitOrder
.Where(t => t.First == 'L')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
var RTree = VisitOrder
.Where(t => t.First == 'R')
.Select((t, i) => String.Format("{0}) {1}", i + 1, t.Second.Name));
Here is how I figured it out:
private class FolderRow
{
public int Indent
{
get { return this.Row.GetValue("Indent", 0); }
set { this.Row["Indent"] = value; }
}
public int Left
{
get { return this.Row.GetValue("Tree L", 0); }
set { this.Row["Tree L"] = value; }
}
public int Right
{
get { return this.Row.GetValue("Tree R", 0); }
set { this.Row["Tree R"] = value; }
}
public Guid Id
{
get { return this.Row.GetValue("FolderID", Guid.Empty); }
}
public DataRow Row { get; private set; }
public int RowNum { get; set; }
public bool RowComplete { get { return this.Left > 0 && this.Right > 0; } }
public FolderRow(DataRow row, int rowNum)
{
this.Row = row;
this.RowNum = rowNum;
}
}
[TestMethod]
public void ImportFolderStructure()
{
var inputTable = FileUtil.GetDataSetFromExcelFile(new FileInfo(#"c:\SampleTree.xlsx")).Tables[0];
var currentLevel = 0;
var nodeCount = 1;
var currentRow = 0;
inputTable.Columns.Add("Indent", typeof (int));
inputTable.Columns.Add("Path", typeof (string));
var rightStack = new List<FolderRow>();
foreach (DataRow row in inputTable.Rows)
row["Indent"] = GetLevelPopulated(row);
foreach (DataRow row in inputTable.Rows)
{
if (row.GetValue("Indent", 0) == 0)
row["Tree L"] = 1;
}
while (true)
{
var row = GetRow(inputTable, currentRow);
if (row.Indent == 0)
{
currentRow++;
rightStack.Add(row);
continue;
}
// if the indent of this row is greater than the previous row ...
if (row.Indent > currentLevel)
{
currentLevel++;
nodeCount++;
row.Left = nodeCount;
// ... check the next row to see if it is further indented
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent == currentLevel)
{
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
} else if (row.Indent < currentLevel)
{
currentLevel--;
nodeCount++;
row.Left = nodeCount;
currentRow = HandleNextRow(row, currentRow, rightStack, inputTable, ref currentLevel, ref nodeCount);
}
if (inputTable.Rows.Cast<DataRow>().Select(r => new FolderRow(r, -1)).All(r => r.RowComplete))
break;
}
}
private int HandleNextRow(FolderRow row, int currentRow, List<FolderRow> rightStack, DataTable inputTable, ref int currentLevel,
ref int nodeCount)
{
var nextRow = GetRow(inputTable, currentRow + 1);
if (nextRow != null)
{
if (nextRow.Indent > row.Indent)
{
// ok the current row has a child so we will need to set the tree right of that, add to stack
rightStack.Add(row);
}
else if (nextRow.Indent == row.Indent)
{
nodeCount++;
row.Right = nodeCount;
}
else if (nextRow.Indent < row.Indent)
{
nodeCount++;
row.Right = nodeCount;
nodeCount++;
// here we need to get the most recently added row to rightStack, set the Right to the current nodeCount
var stackLast = rightStack.LastOrDefault();
if (stackLast != null)
{
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
currentLevel--;
}
currentRow++;
if (rightStack.Count > 1)
{
// before we move on to the next row, we need to check if there is more than one row still in right stack and
// the new current row's ident is less than the current branch (not current leaf)
var newCurrentRow = GetRow(inputTable, currentRow);
var stackLast = rightStack.LastOrDefault();
if (newCurrentRow.Indent == stackLast.Indent)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
}
}
}
else
{
// reached the bottom
nodeCount++;
row.Right = nodeCount;
// loop through the remaining items in rightStack and set their right values
var stackLast = rightStack.LastOrDefault();
while (stackLast != null)
{
nodeCount++;
stackLast.Right = nodeCount;
rightStack.Remove(stackLast);
stackLast = rightStack.LastOrDefault();
}
}
return currentRow;
}
private FolderRow GetRow(DataTable inputTable, int rowCount)
{
if (rowCount >= inputTable.Rows.Count)
return null;
return rowCount < 0 ? null : new FolderRow(inputTable.Rows[rowCount],rowCount);
}
private int GetLevelPopulated(DataRow row)
{
var level = 0;
while (level < 14)
{
if (level == 0 && row["Root"] != DBNull.Value)
return level;
level++;
if (row["Level " + level] != DBNull.Value)
return level;
}
return -1;
}