Finding all possible paths in a cyclic directed graph given a starting vertex and a depth limitation - algorithm

Consider the directed cyclic graph given below;
If a starting point (eg: vertex 0) and a maximum depth allowed is specified (eg: 5), what algorithm can be used to find all possible paths (note: a given vertex can be visited more than once)?
What is the most efficient algorithm to implement this graph problem?
Some of the possible paths for the above graph are given below in no particular order (starting with vertex 0 and maximum depth allowed is 5).
0 -> 1 -> 2 -> 4 -> 1 -> 3
0 -> 1 -> 2 -> 4 -> 5 -> 1
0 -> 1 -> 2 -> 4 -> 5 -> 6
0 -> 1 -> 3 -> 5 -> 1 -> 3

Pseudo algorithm for this will be an augmented BFS that keeps track of the path it has gone through. When it hits the required depth, it registers the path and then terminates.
Something like this (node.js style syntax):
const requiredDepth = X
const relevantPaths = {}
const runBFS = (curNode, curPath = []) => {
if (crPath.length === requiredDepth) {
relevantPaths.push(curPath)
return
}
for (let neighbor of curNode.neighbors) {
const newPath = [ ...curPath, getEdge(curNode, neighbor) ]
runBFS(neighbor, newPath)
}
}
runBFS(root)
Hope this helps

Related

Random position of nodes in hierarchical tree with alternate paths

I'm struggling with directed graph hierarchy tree visualization (distributed network in this case). Using graphviz: dot I have this tree:
digraph G {
node[width=0.14, height=0.14];
edge[weight=3, color = "0.000 0.000 0.0"];
1 -> 2;
1 -> 3;
edge[weight=2, color = "0.000 0.000 0.175"];
2 -> 4;
2 -> 5;
3 -> 6;
3 -> 7;
edge[weight=1, color = "0.000 0.000 0.825"];
3 -> 4;
3 -> 5;
2 -> 6;
2 -> 7;
}
Resulting in:
You can see that the primary paths (edges with higher weights) are crossed. The goal is to have the preferred path nodes as close as possible. I can do it by changing the order of nodes randomly so the output looks like this:
However I don't want to think about the ordering algorithm as I want to automate this on hundreds of nodes.
From documentation:
The weight of an edge provides another way to keep edges straight. An edge’s weight suggests some measure of an edge’s importance; thus, the heavier the weight, the closer together its nodes should be. dot causes edges with heavier weights to be drawn shorter and straighter.
But this is not the case as the nodes are drawn in random order. What am I doing wrong?
Probably not the answer you were looking for, but since dot doesn't seem to work with edge weights in this case:
You may just use constraint=false for the lightgray edges.
Could you live with a solution like this, which places invisible ranks and invisible edges to enforce the left-to-right ordering of your nodes? While it adds some white space to the left of the diagram, at least it neatly solves the random-ordering issue within each rank, and lends itself to being automated.
digraph G {
node[width=0.14, height=0.14];
{
rankdir="TB";
edge [style=invis];
rank1 [style=invis];
rank2 [style=invis];
rank3 [style=invis];
rank1 -> rank2 -> rank3 [style=invis];
}
edge[weight=3, color = "0.000 0.000 0.0"];
1 -> 2;
1 -> 3;
edge[weight=2, color = "0.000 0.000 0.175"];
2 -> 4;
2 -> 5;
3 -> 6;
3 -> 7;
{
edge [style=invis];
rank=same;
rank2 -> 2 -> 3;
}
edge[weight=1, color = "0.000 0.000 0.825"];
3 -> 4;
3 -> 5;
2 -> 6;
2 -> 7;
{
rank=same;
edge [style=invis];
rank3 -> 4 -> 5 -> 6 -> 7 ;
}
}

Minimum Hamiltonian path length using brute force approach

Assumption At least one Hamiltonian path exists in the graph. I am trying to find minimum path length among all Hamiltonian paths.
My approach
Let us say we have three nodes.
Possible paths are
1 -> 2 -> 3
1 -> 3 -> 2
2 -> 1 -> 3
2 -> 3 -> 1
3 -> 1 -> 2
3 -> 2 -> 1
Find path length of all tracks and take minimum among them. Time complexity of this approach will be O(N*(N!)) where N = #nodes
I am getting the wrong answer with this approach. Is the above approach correct? Please help.

Bellman-Ford algorithm proof of correctness

I'm trying to learn about Bellman-Ford algorithm but I'm stucked with the proof of the correctness.
I have used Wikipedia, but I simply can't understand the proof. I did not find anything on Youtube that's helpfull.
Hope anyone of you can explain it briefly. This page "Bellman-ford correctness can we do better" does not answer my question.
Thank you.
Let's see the problem from the perspective of dynamic programming of a graph with no negative cycle.
We can visualize the memoization table of the dynamic programming as follows:
The columns represent nodes and the rows represent update steps(node 0 is the source node), and the arrows directing from one box in a step to another in the next step are the min updates(step 0 is the initialization).
We choose one path from all shortest paths and illustrate why it is correct. Let's choose the 0 -> 3 -> 2 -> 4 -> 5. It is the shortest path from 0 to 5, we can choose any other one otherwise. We can prove the correctness by reduction. The initial is the source 0, and obviously, the distance between 0 and itself should be 0, the shortest. And we assume 0 -> 3 -> 2 is the shortest path between 0 and 2, and we are going to prove that 0 -> 3 -> 2 -> 4 is the shortest path between 0 and 4 after the third iteration.
First, we prove that after the third iteration the node 4 must be fixed/tightened. If node 4 is not fixed it means that there is at least one path other than 0 -> 3 -> 2 -> 4 that can reach 4 and that path should be shorter than 0 -> 3 -> 2 -> 4, which contradicts our assumption that 0 -> 3 -> 2 -> 4 -> 5 is the shortest path between 0 and 5. Then after the third iteration, 2 and 4 should be connected.
Second, we prove that that relaxation should be the shortest. It cannot be greater and smaller because it is the only shortest path.
Let's see a graph with a negative cycle.
And here is its memoization table:
Let's prove that at |V|'s iteration, here |V| is the number of vertices 6, the update should not be stopped.
We assume that the update stopped(and there is a negative cycle). Let's see the cycle 3 -> 2 -> 4 -> 5 -> 3.
dist(2) <= dist(3) + w(3, 2)
dist(4) <= dist(2) + w(2, 4)
dist(5) <= dist(4) + w(4, 5)
dist(3) <= dist(5) + w(5, 3)
And we can obtain the following inequlity from the above four inequalities by summing up the left-hand side and the right-hand side:
dist(2) + dist(4) + dist(5) + dist(3) <= dist(3) + dist(2) + dist(4) + dist(5) + w(3, 2) + w(2, 4) + w(4, 5) + w(5, 3)
We subtract the distances from both sides and obtain that:
w(3, 2) + w(2, 4) + w(4, 5) + w(5, 3) >= 0, which contradicts our claim that 3 -> 2 -> 4 -> 5 -> 3 is a negative cycle.
So we are certain that at |V|'s step and after that step the update would never stop.
My code is here on Gist.
Reference:
dynamic programming - bellman-ford algorithm
Lecture 14: Bellman-Ford Algorithm

Pairwise swap of linkedlist

I have been trying to do pairwise swap of linkedlist elements. In place of swapping the elements by data, I am swapping them by swapping the links.
C# code:
public LinkedList pairWiseSwapLinks(LinkedList ll)
{
LinkedList curr = ll;
LinkedList next = curr.nextNode;
ll = curr;
while (curr.nextNode != null && next.nextNode != null)
{
curr.nextNode = next.nextNode;
next.nextNode = curr;
Console.WriteLine(curr.data);
Console.WriteLine(next.data);
curr = curr.nextNode;
next = curr.nextNode;
Console.WriteLine(curr.data);
Console.WriteLine(next.data);
}
return ll;
}
The input is: 1 -> 3 -> 10 -> 14 -> 16 -> 20 -> 40
Output: 1 -> 10 -> 16 -> 40
Can someone help me out with what mistake I am making?
There are 2 issues:
after all swaps, your first node should be 3 instead of 1 in your case, so you should return the originally second node (what if there's only 1?).
The reason why 14 is skipped in your case is because you only involve 2 nodes as a pair in each swap. So what happens after the first swap is that the list becomes 3 -> 1 -> 10 -> 16 ... and 14 -> 10 -> 16 so essentially you've lost 14 (i.e. you didn't change the "nextNode" ref in node 1, which should be pointed to 14 in that case).
I don't want to give you the direct solution here but I can give you some hints:
You need to involve 3 nodes in each swap.
However, what if there are only 1/2 nodes?
Adding all the corner cases into your code would make it kinda unreadable, so what if I add a dummy node to the head of the list so eventually I can just write "return dummy.next" instead of bothering finding the current first node?

Finding the root value of a binary tree?

I have an array which stores the relations of values, which makes several trees something like:
So, in this case, my array would be (root, linked to)
(8,3)
(8,10)
(3,1)
(3,6)
(6,4)
(6,7)
(10,14)
(14,13)
And i'd like to set all the root values in the array to the main root in the tree (in all trees):
(8,3)
(8,1)
(8,6)
(8,4)
(8,7)
(8,10)
(8,14)
(8,13)
What algorithm should i investigate?
1) Make a list of all the unique first elements of the tuples.
2) Remove any that also appear as the second element of a tuple.
3) You'll be left with the root (8 here). Replace the first elements of all tuples with this value.
EDIT:
A more complicated approach that will work with multiple trees would be as follows.
First, convert to a parent lookup table:
1 -> 3
3 -> 8
4 -> 6
6 -> 3
7 -> 6
10 -> 8
13 -> 14
14 -> 10
Next, run "find parent with path compression" on each element:
1)
1 -> 3 -> 8
gives
1 -> 8
3 -> 8
4 -> 6
...
3)
3 -> 8
4)
4 -> 6 -> 3 -> 8
gives
1 -> 8
3 -> 8
4 -> 8
6 -> 8
7 -> 6
...
6)
6 -> 8 (already done)
7)
7 -> 6 -> 8
etc.
Result:
1 -> 8
3 -> 8
4 -> 8
6 -> 8
7 -> 8
...
Then convert this back to the tuple list:
(8,1)(8,3)(8,4)...
The find parent with path compression algorithm is as find_set would be for disjoint set forests, e.g.
int find_set(int x) const
{
Element& element = get_element(x);
int& parent = element.m_parent;
if(parent != x)
{
parent = find_set(parent);
}
return parent;
}
The key point is that path compression helps you avoid a lot of work. In the above, for example, when you do the lookup for 4, you store 6 -> 8, which makes later lookups referencing 6 faster.
So assume you have a list of tuples representing the points:
def find_root(ls):
child, parent, root = [], [], []
for node in ls:
parent.append(node[0])
child.append(node[1])
for dis in parent:
if (!child.count(dis)):
root.append(dis)
if len(root) > 1 : return -1 # failure, the tree is not formed well
for nodeIndex in xrange(len(ls)):
ls[nodeIndex] = (root[0], ls[nodeIndex][1])
return ls

Resources