Best way to find the most costly path in graph - algorithm

I have a directed acyclic graph on which every vertex has a weight >= 0. There is a vertex who is the "start" of the graph and another vertex who is the "end" of the graph. The idea is to find the path from the start to the end whose sum of the weights of the vertices is the greater. For example, I have the next graph:
I(0) -> V1(3) -> F(0)
I(0) -> V1(3) -> V2(1) -> F(0)
I(0) -> V3(0.5) -> V2(1) -> F(0)
The most costly path would be I(0) -> V1(3) -> V2(1) -> F(0), which cost is 4.
Right now, I am using BFS to just enumerate every path from I to F as in the example above, and then, I choose the one with the greatest sum. I am afraid this method can be really naive.
Is there a better algorithm to do this? Can this problem be reduced to another one?

Since your graph has no cycles* , you can negate the weights of your edges, and run Bellman-Ford's algorithm.
* Shortest path algorithms such as Floyd-Warshall and Bellman-Ford do not work on graphs with negative cycles, because you can build a path of arbitrarily small weight by staying in a negative cycle.

You can perform a topological sort, then iterate through the list of vertices returned by the topological sort, from the start vertex to the end vertex and compute the costs. For each directed edge of the current vertex check if you can improve the cost of destination vertex, then move to the next one. At the end cost[end_vertex] will contain the result.
class grph:
def __init__(self):
self.no_nodes = 0
self.a = []
def build(self, path):
file = open(path, "r")
package = file.readline()
self.no_nodes = int(package[0])
self.a = []
for i in range(self.no_nodes):
self.a.append([10000] * self.no_nodes)
for line in file:
package = line.split(' ')
source = int(package[0])
target = int(package[1])
cost = int(package[2])
self.a[source][target] = cost
file.close()
def tarjan(graph):
visited = [0] * graph.no_nodes
path = []
for i in range(graph.no_nodes):
if visited[i] == 0:
if not dfs(graph, visited, path, i):
return []
return path[::-1]
def dfs(graph, visited, path, start):
visited[start] = 1
for i in range(graph.no_nodes):
if graph.a[start][i] != 10000:
if visited[i] == 1:
return False
elif visited[i] == 0:
visited[i] = 1
if not dfs(graph, visited, path, i):
return False
visited[start] = 2
path.append(start)
return True
def lw(graph, start, end):
topological_sort = tarjan(graph)
costs = [0] * graph.no_nodes
i = 0
while i < len(topological_sort) and topological_sort[i] != start:
i += 1
while i < len(topological_sort) and topological_sort[i] != end:
vertex = topological_sort[i]
for destination in range(graph.no_nodes):
if graph.a[vertex][destination] != 10000:
new_cost = costs[vertex] + graph.a[vertex][destination]
if new_cost > costs[destination]:
costs[destination] = new_cost
i += 1
return costs[end]
Input file:
6
0 1 6
1 2 2
3 0 10
1 4 4
2 5 9
4 2 2
0 2 10

In general longest path problem is NP-hard, but since the graph is a DAG, it can be solved by first negating the weights then do a shortest path. See here.
Because the weights reside on the vertices, before computing, you might want to move the weights to the in edges of the vertices.

Related

Minimum time to reach from city 1 to city N [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
There are a lot of graph problems that require some modification of the BFS algorithm. I just come across this problem. I thought of this question with just an extension of the standard BFS algorithm.
The question states that:
We are given a country, having N cities and M bidirectional roads. Each city has a traffic light, showing only 2 colors i.e Green and Red. All the traffic lights switch their color from Green to Red or vice versa after every T seconds. We can cross a city only when the traffic light is green. Initially, all traffic light is green. In any city, if the traffic light is Red then we have to wait for its light to turn green. Time taken to travel any road is C. We have to find minimum time to reach City N from 1.
Note: graph doesn't contain a self-loop or multiple edges.
For example:
N=5,M=5,T=3,C=5
Edges are:
1 2,
1 3,
2 4,
1 4,
2 5.
Here minimum time to go from 1 to 5 is 11 through path 1 2 5.WE can reach city 2 in 5 secs. then wait for 1 second for the light to turn green and then 5 seconds to go to 5.
Can anyone share his approach toward this problem? whether it is a BFS problem or some other graph algorithm required too?
Better to unsderstand if psedoucode will be there along with algorithm?
Because all the cities start with same initial state, switch lights with the same frequency, and all the roads have the same duration, the traffic lights delay all routes equally.
As all roads have the same duration, this means that BFS will be an efficient way to solve the problem. The only adjustment to the standard algorithm is to adjust the time at each node to account for any delay due to the traffic lights.
(If the roads had different durations, or the lights switched irregularly, then a more advanced algorithm such as Dijkstra would be required.)
I'm assuming all edge weights have an integer number of seconds.
Note that the period of a traffic light is 2T. Take your original graph G and duplicate its nodes 2T times: G0, G1, ..., G2T - 1. If there is an edge in the original graph G from node a to b with weight w, then add an edge with weight w from each node a in Gt to b in G(t + w) mod 2T for each t where the light in a is green. Add an edge with weight 1 between each respective node in Gt, G(t+1) mod 2T, representing the possibility to wait at a city.
Finally, add one more copy of the nodes of G to your graph, D, that will be used for the destination nodes. Add an edge from each node in Gt to its respective node in D with weight 0.
Then the shortest path between nodes s in G0 and t in D follows exactly your problem.
#include <bits/stdc++.h>
using namespace std;
#define ll long long int
vector<int> g[1001];
vector<pair<ll,vector<ll>>> pt;
void dfs(ll st,ll e,ll vis[],vector<ll> rs,ll w){
rs.push_back(st);
if(st == e){
pt.push_back({w*(rs.size()-1),rs});
return;
}
for(auto u : g[st]){
if(vis[u] == 0){
vis[st] = 1;
dfs(u,e,vis,rs,w);
vis[st] = 0;
}
}
}
int main()
{
ll n,m,t,c,u,v;
cin>>n>>m>>t>>c;
while(m--){
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
if(n == 1)
cout<<0<<endl;
else if(n == 2)
cout<<t<<endl;
else{
vector<ll> rs;
ll w = c;
ll vis[n+1] = {0};
dfs(1,n,vis,rs,w);
if(pt.size() == 0)
cout<<-1<<endl;
else{
sort(pt.begin(),pt.end());
ll te = 0;
ll nt = 0;
for(int i=1;i<pt[0].second.size();i++){
te += c + (nt-te);
while(nt < te)
nt += t;
}
cout<<te<<endl;
}//else
}
return 0;
}
This Question was asked in a coding round of a company that i attended, I could come up with a BFS solution with O(n) Time Complexity.
(The coding round is over now. So here's my solution)-Edited
My Solution (Python 3):
def getPresentTrafficColorAndWaitTime(curr_color,traffic,cost_travel):
#color
val = (cost_travel//traffic)%2
if val == 1:
curr_color = 0 if curr_color==1 else 1
#waitTime
waitTime = traffic - (cost_travel%traffic)
return curr_color,waitTime
def bfs(adj,visited,curr,final,traffic,cost_travel):
queue = []
queue.append(1)
visited[1] = 1
dist = 0
# Red: 1 , Green: 0
curr_color = 0
temp_cost_travel = cost_travel
while queue:
length = len(queue)
for _ in range(length):
curr = queue[0]
queue.pop(0)
for i in adj[curr]:
if i == final:
return dist+cost_travel
elif visited[i] == 0:
visited[i] = 1
queue.append(i)
curr_color,waitTime = getPresentTrafficColorAndWaitTime(curr_color,traffic,temp_cost_travel)
if curr_color == 1:
add_dist = waitTime
temp_cost_travel = cost_travel
curr_color = 0
else:
add_dist = 0
temp_cost_travel += cost_travel
dist += cost_travel + add_dist
return -1
# Taking Input and creating Adjacency-list using defaultdict
n,m,t,c = map(int,input().split())
adj_list = defaultdict(list)
for _ in range(m):
u,v = map(int,input().split())
adj_list[u] = adj_list.get(u,[]) + [v]
adj_list[v] = adj_list.get(v,[]) + [u]
visited = [0]*(n+1)
answer = bfs(adj_list,visited,1,n,t,c)
print(answer)

Print the elements which making min cost path from a start point to end point in a grid

We can calculate min cost suppose take this recurrence relation
min(mat[i-1][j],mat[i][j-1])+mat[i][j];
0 1 2 3
4 5 6 7
8 9 10 11
for calculating min cost using the above recurrence relation we will get for min-cost(1,2)=0+1+2+6=9
i am getting min cost sum, that's not problem..now i want to print the elements 0,1,2,6 bcz this elements are making min cost path.
Any help is really appreciated.
Suppose, your endpoint is [x, y] and start-point is [a, b]. After the recursion step, now start from the endpoint and crawl-back/backtrack to start point.
Here is the pseudocode:
# Assuming grid is the given input 2D grid
output = []
p = x, q = y
while(p != a && q != b):
output.add(grid[p][q])
min = infinity
newP = -1, newQ = -1
if(p - 1 >= 0 && mat[p - 1][q] < min):
min = matrix[p -1][q]
newP = p - 1
newQ = q
if(q - 1 >= 0 && mat[p][q - 1] < min):
min = mat[p][q - 1]
newP = p
newQ = q - 1
p = newP, q = newQ
end
output.add(grid[a][b])
# print output
Notice, here we used mat and grid - two 2D matrix where grid is the given input and mat is the matrix generated after the recursion step mat[i][j] = min(mat[i - 1][j], mat[i][j - 1]) + grid[i][j]
Hope it helps!
Besides computing the min cost matrix using the relation that you mentioned, you can also create a predecessor matrix.
For each cell (i, j), you should also store the information about who was the "min" in the relation that you mentioned (was it the left element, or is it the element above?). In this way, you will know for each cell, which is its preceding cell in an optimal path.
Afterwards, you can generate the path by starting from the final cell and moving backwards according to the "predecessor" matrix, until you reach the top-left cell.
Note that the going backwards idea can be applied also without explicitly constructing a predecessor matrix. At each point, you would need to look which of the candidate predecessors has a lower total cost.

Algorithm to group items in groups of 3

I am trying to solve a problem where I have pairs like:
A C
B F
A D
D C
F E
E B
A B
B C
E D
F D
and I need to group them in groups of 3 where I must have a triangule of matching from that list. Basically I need a result if its possible or not to group a collection.
So the possible groups are (ACD and BFE), or (ABC and DEF) and this collection is groupable since all letters can be grouped in groups of 3 and no one is left out.
I made a script where I can achieve this for small ammounts of input but for big ammounts it gets too slow.
My logic is:
make nested loop to find first match (looping untill I find a match)
> remove 3 elements from the collection
> run again
and I do this until I am out of letters. Since there can be different combinations I run this multiple times starting on different letters until I find a match.
I can understand that this gives me loops in order at least N^N and can get too slow. Is there a better logic for such problems? can a binary tree be used here?
This problem can be modeled as a graph Clique cover problem. Every letter is a node and every pair is an edge and you want to partition the graph into vertex-disjoint cliques of size 3 (triangles). If you want the partitioning to be of minimum cardinality then you want a minimum clique cover.
Actually this would be a k-clique cover problem, because in the clique cover problem you can have cliques of arbitrary/different sizes.
As Alberto Rivelli already stated, this problem is reducible to the Clique Cover problem, which is NP-hard.
It is also reducible to the problem of finding a clique of particular/maximum size. Maybe there are others, not NP-hard problems to which your particular case could be reduced to, but I didn't think of any.
However, there do exist algorithms which can find the solution in polynomial time, although not always for worst cases. One of them is Bron–Kerbosch algorithm, which is known by far to be the most efficient algorithm for finding the maximum clique and can find a clique in the worst case of O(3^(n/3)). I don't know the size of your inputs, but I hope it will be sufficient for your problem.
Here is the code in Python, ready to go:
#!/usr/bin/python3
# #by DeFazer
# Solution to:
# stackoverflow.com/questions/40193648/algorithm-to-group-items-in-groups-of-3
# Input:
# N P - number of vertices and number of pairs
# P pairs, 1 pair per line
# Output:
# "YES" and groups themselves if grouping is possible, and "NO" otherwise
# Input example:
# 6 10
# 1 3
# 2 6
# 1 4
# 4 3
# 6 5
# 5 2
# 1 2
# 2 3
# 5 4
# 6 4
# Output example:
# YES
# 1-2-3
# 4-5-6
# Output commentary:
# There are 2 possible coverages: 1-2-3*4-5-6 and 2-5-6*1-3-4.
# If required, it can be easily modified to return all possible groupings rather than just one.
# Algorithm:
# 1) List *all* existing triangles (1-2-3, 1-3-4, 2-5-6...)
# 2) Build a graph where vertices represent triangles and edges connect these triangles with no common... vertices. Sorry for ambiguity. :)
# 3) Use [this](en.wikipedia.org/wiki/Bron–Kerbosch_algorithm) algorithm (slightly modified) to find a clique of size N/3.
# The grouping is possible if such clique exists.
N, P = map(int, input().split())
assert (N%3 == 0) and (N>0)
cliquelength = N//3
pairs = {} # {a:{b, d, c}, b:{a, c, f}, c:{a, b}...}
# Get input
# [(0, 1), (1, 3), (3, 2)...]
##pairlist = list(map(lambda ab: tuple(map(lambda a: int(a)-1, ab)), (input().split() for pair in range(P))))
pairlist=[]
for pair in range(P):
a, b = map(int, input().split())
if a>b:
b, a = a, b
a, b = a-1, b-1
pairlist.append((a, b))
pairlist.sort()
for pair in pairlist:
a, b = pair
if a not in pairs:
pairs[a] = set()
pairs[a].add(b)
# Make list of triangles
triangles = []
for a in range(N-2):
for b in pairs.get(a, []):
for c in pairs.get(b, []):
if c in pairs[a]:
triangles.append((a, b, c))
break
def no_mutual_elements(sortedtupleA, sortedtupleB):
# Utility function
# TODO: if too slow, can be improved to O(n) since tuples are sorted. However, there are only 9 comparsions in case of triangles.
return all((a not in sortedtupleB) for a in sortedtupleA)
# Make a graph out of that list
tgraph = [] # if a<b and (b in tgraph[a]), then triangles[a] has no common elements with triangles[b]
T = len(triangles)
for t1 in range(T):
s = set()
for t2 in range(t1+1, T):
if no_mutual_elements(triangles[t1], triangles[t2]):
s.add(t2)
tgraph.append(s)
def connected(a, b):
if a > b:
b, a = a, b
return (b in tgraph[a])
# Finally, the magic algorithm!
CSUB = set()
def extend(CAND:set, NOT:set) -> bool:
# while CAND is not empty and there is no vertex in NOT connected to *all* vertexes in CAND
while CAND and all((any(not connected(n, c) for c in CAND)) for n in NOT):
v = CAND.pop()
CSUB.add(v)
newCAND = {c for c in CAND if connected(c, v)}
newNOT = {n for n in NOT if connected(n, v)}
if (not newCAND) and (not newNOT) and (len(CSUB)==cliquelength): # the last condition is the algorithm modification
return True
elif extend(newCAND, newNOT):
return True
else:
CSUB.remove(v)
NOT.add(v)
if extend(set(range(T)), set()):
print("YES")
# If the clique itself is not needed, it's enough to remove the following 2 lines
for a, b, c in [triangles[c] for c in CSUB]:
print("{}-{}-{}".format(a+1, b+1, c+1))
else:
print("NO")
If this solution is still too slow, perphaps it may be more efficient to solve the Clique Cover problem instead. If that's the case, I can try to find a proper algorithm for it.
Hope that helps!
Well i have implemented the job in JS where I feel most confident. I also tried with 100000 edges which are randomly selected from 26 letters. Provided that they are all unique and not a point such as ["A",A"] it resolves in like 90~500 msecs. The most convoluted part was to obtain the nonidentical groups, those without just the order of the triangles changing. For the given edges data it resolves within 1 msecs.
As a summary the first reduce stage finds the triangles and the second reduce stage groups the disconnected ones.
function getDisconnectedTriangles(edges){
return edges.reduce(function(p,e,i,a){
var ce = a.slice(i+1)
.filter(f => f.some(n => e.includes(n))), // connected edges
re = []; // resulting edges
if (ce.length > 1){
re = ce.reduce(function(r,v,j,b){
var xv = v.find(n => e.indexOf(n) === -1), // find the external vertex
xe = b.slice(j+1) // find the external edges
.filter(f => f.indexOf(xv) !== -1 );
return xe.length ? (r.push([...new Set(e.concat(v,xe[0]))]),r) : r;
},[]);
}
return re.length ? p.concat(re) : p;
},[])
.reduce((s,t,i,a) => t.used ? s
: (s.push(a.map((_,j) => a[(i+j)%a.length])
.reduce((p,c,k) => k-1 ? p.every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, p.push(c),p) : p
: [p].every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, [p,c]) : [p])),s)
,[]);
}
var edges = [["A","C"],["B","F"],["A","D"],["D","C"],["F","E"],["E","B"],["A","B"],["B","C"],["E","D"],["F","D"]],
ps = 0,
pe = 0,
result = [];
ps = performance.now();
result = getDisconnectedTriangles(edges);
pe = performance.now();
console.log("Disconnected triangles are calculated in",pe-ps, "msecs and the result is:");
console.log(result);
You may generate random edges in different lengths and play with the code here

Removing edge that causes a cycle in an undirected graph

I have a graph that is represented by an adjacency matrix, G and I am trying to use DFS to remove an edge that causes a cycle
There can be multiple cycles, but I figure it is probably best to remove them one at a time, so I only need my algorithm to find one cycle, and that can be repeated.
Here is the code for what I have got so far:
function [ G, c_flag, c_stack, o_stack, cptr, optr ] =...
dfs_cycle( G, curr_v, c_stack, o_stack, cptr, optr, c_flag )
% add current vertex to open list
optr = optr + 1;
o_stack(optr) = curr_v;
% find adjacent vertices
adj_v = find(G(curr_v,:));
for next_v = adj_v
% ensure next_v is not in closed list
if ~any(c_stack == next_v)
% if next_v in open list then cycle exists
if any(o_stack == next_v)
% remove edge and set flag to 1
G(curr_v, next_v) = 0;
G(next_v, curr_v) = 0;
c_flag = 1;
break;
end
[G, c_flag, c_stack, o_stack, cptr, optr] =...
dfs_cycle(G, next_v, c_stack, o_stack, cptr, optr, c_flag);
if c_flag == 1
break;
end
% remove vertex from open list and put into closed list
o_stack(optr) = 0;
optr = optr - 1;
cptr = cptr + 1;
c_stack(cptr) = next_v;
end
end
end
the function is called using:
v_list = find(sum(G)>0);
o_stack = zeros(1,numel(v_list));
c_stack = o_stack;
optr = 0;
cptr = 0;
root_v = v_list(randperm(length(v_list),1));
c_flag = 0;
[G_dash,c_flag,~,~,~,~] =...
dfs_cycle(G, root_v, c_stack, o_stack, cptr, optr, c_flag);
It should return the modified (if cycle found) adjacency matrix, G_dash and c_flag corresponding to whether a cycle was found or not.
However, it doesnt seem to be functioning as it should.
I think I have located the problem; in the line if any(o_stack == next_v) it will return true, because the previous vertex visited is usually still in o_stack, however I am not sure how I should go about fixing this.
Does anyone have any ideas?
A connected, un-directed, acyclic graph is called a tree, with n nodes and n - 1 edges. For a formal proof, see here.
So, to form a tree from your graph, you just need to run DFS once, and keep all edges used by this DFS (for more information about tree created by DFS, see wiki link, example section). Those unused edges can be removed.

How to easily know if a maze has a road from start to goal?

I implemented a maze using 0,1 array. The entry and goal is fixed in the maze. Entry always be 0,0 point of the maze. Goal always be m-1,n-1 point of the maze. I'm using breadth-first search algorithm for now, but the speed is not good enough. Especially for large maze (100*100 or so). Could someone help me on this algorithm?
Here is my solution:
queue = []
position = start_node
mark_tried(position)
queue << position
while(!queue.empty?)
p = queue.shift #pop the first element
return true if maze.goal?(p)
left = p.left
visit(queue,left) if can_visit?(maze,left)
right = p.right
visit(queue,right) if can_visit?(maze,right)
up = p.up
visit(queue,up) if can_visit?(maze,up)
down = p.down
visit(queue,down) if can_visit?(maze,down)
end
return false
the can_visit? method check whether the node is inside the maze, whether the node is visited, whether the node is blocked.
worst answer possible.
1) go front until you cant move
2) turn left
3) rinse and repeat.
if you make it out , there is an end.
A better solution.
Traverse through your maze keeping 2 lists for open and closed nodes. Use the famous A-Star algorithm
to choose evaluate the next node and discard nodes which are a dead end. If you run out of nodes on your open list, there is no exit.
Here is a simple algorithm which should be much faster:
From start/goal move to to the first junction. You can ignore anything between that junction and the start/goal.
Locate all places in the maze which are dead ends (they have three walls). Move back to the next junction and take this path out of the search tree.
After you have removed all dead ends this way, there should be a single path left (or several if there are several ways to reach the goal).
I would not use the AStar algorithm there yet, unless I really need to, because this can be done with some simple 'coloring'.
# maze is a m x n array
def canBeTraversed(maze):
m = len(maze)
n = len(maze[0])
colored = [ [ False for i in range(0,n) ] for j in range(0,m) ]
open = [(0,0),]
while len(open) != 0:
(x,y) = open.pop()
if x == m-1 and y == n-1:
return True
elif x < m and y < n and maze[x][y] != 0 not colored[x][y]:
colored[x][y] = True
open.extend([(x-1,y), (x,y-1), (x+1,y), (x,y+1)])
return False
Yes it's stupid, yes it's breadfirst and all that.
Here is the A* implementation
def dist(x,y):
return (abs(x[0]-y[0]) + abs(x[1]-y[1]))^2
def heuristic(x,y):
return (x[0]-y[0])^2 + (x[1]-y[1])^2
def find(open,f):
result = None
min = None
for x in open:
tmp = f[x[0]][x[1]]
if min == None or tmp < min:
min = tmp
result = x
return result
def neighbors(x,m,n):
def add(result,y,m,n):
if x < m and y < n: result.append(y)
result = []
add(result, (x[0]-1,x[1]), m, n)
add(result, (x[0],x[1]-1), m, n)
add(result, (x[0]+1,x[1]), m, n)
add(result, (x[0],x[1]+1), m, n)
return result
def canBeTraversedAStar(maze):
m = len(maze)
n = len(maze[0])
goal = (m-1,n-1)
closed = set([])
open = set([(0,0),])
g = [ [ 0 for y in range(0,n) ] for x in range(0,m) ]
h = [ [ heuristic((x,y),goal) for y in range(0,n) ] for x in range(0,m) ]
f = [ [ h[x][y] for y in range(0,n) ] for x in range(0,m) ]
while len(open) != 0:
x = find(open,f)
if x == (m-1,n-1):
return True
open.remove(x)
closed.add(x)
for y in neighbors(x,m,n):
if y in closed: continue
if y not in open:
open.add(y)
g[y[0]][y[1]] = g[x[0]][x[1]] + dist(x,y)
h[y[0]][y[1]] = heuristic(y,goal)
f[y[0]][y[1]] = g[y[0]][y[1]] + h[y[0]][y[1]]
return True
Here is my (simple) benchmark code:
def tryIt(func,size, runs):
maze = [ [ 1 for i in range(0,size) ] for j in range(0,size) ]
begin = datetime.datetime.now()
for i in range(0,runs): func(maze)
end = datetime.datetime.now()
print size, 'x', size, ':', (end - begin) / runs, 'average on', runs, 'runs'
tryIt(canBeTraversed,100,100)
tryIt(canBeTraversed,1000,100)
tryIt(canBeTraversedAStar,100,100)
tryIt(canBeTraversedAStar,1000,100)
Which outputs:
# For canBeTraversed
100 x 100 : 0:00:00.002650 average on 100 runs
1000 x 1000 : 0:00:00.198440 average on 100 runs
# For canBeTraversedAStar
100 x 100 : 0:00:00.016100 average on 100 runs
1000 x 1000 : 0:00:01.679220 average on 100 runs
The obvious here: going A* to run smoothly requires a lot of optimizations I did not bother to go after...
I would say:
Don't optimize
(Expert only) Don't optimize yet
How much time are you talking about when you say too much ? Really a 100x100 grid is so easily parsed in brute force it's a joke :/
I would have solved this with an AStar implementation. If you want even more speed, you can optimize to only generate the nodes from the junctions rather than every tile/square/step.
A method you can use that does not need to visit all nodes in the maze is as follows:
create an integer[][] with one value per maze "room"
create a queue, add [startpoint, count=1, delta=1] and [goal, count=-1, delta=-1]
start coloring the route by:
popping an object from the head of the queue, put the count at the maze point.
check all reachable rooms for a count with sign opposite to that of the rooms delta, if you find one the maze is solved: run both ways and connect the routes with the biggest steps up and down in room counts.
otherwise add all reachable rooms that have no count to the tail of the queue, with delta added to the room count.
if the queue is empty no path through the maze is possible.
This not only determines if there is a path, but also shows the shortest path possible through the maze.
You don't need to backtrack, so its O(number of maze rooms)

Resources