I have a doubt related to backtracking using Recursion and backtracking using a Stack.
For example in the Evaluate Division question, https://leetcode.com/problems/evaluate-division/
In the recursive approach, when we enter a recursion we first mark the node as visited and then unmark it. This is the approach 1 in the code and is also the LC solution for this problem.
However, when using a stack it was never required to unmark the node.
If you have a look at my approach 2, whenever I pop out a node from the stack I mark it as visited.
What is the conceptual difference between these 2 approaches ?
Can we avoid unmarking the node using Recursion as well ?
Approach 1: LC solution
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
HashMap<String, HashMap<String, Double>> graph = new HashMap<>();
// Step 1). build the graph from the equations
for (int i = 0; i < equations.size(); i++) {
List<String> equation = equations.get(i);
String dividend = equation.get(0), divisor = equation.get(1);
double quotient = values[i];
if (!graph.containsKey(dividend)) {
graph.put(dividend, new HashMap<String, Double>());
}
if (!graph.containsKey(divisor)) {
graph.put(divisor, new HashMap<String, Double>());
}
graph.get(dividend).put(divisor, quotient);
graph.get(divisor).put(dividend, 1 / quotient);
}
// Step 2). Evaluate each query via bactracking (DFS)
// by verifying if there exists a path from dividend to divisor
double[] results = new double[queries.size()];
for (int i = 0; i < queries.size(); i++) {
List<String> query = queries.get(i);
String dividend = query.get(0), divisor = query.get(1);
if (!graph.containsKey(dividend) || !graph.containsKey(divisor)) { // graph does not have source or destination node.
results[i] = -1.0;
} else if (dividend == divisor) {
results[i] = 1.0;
} else {
HashSet<String> visited = new HashSet<>();
results[i] = backtrackEvaluate(graph, dividend, divisor, 1, visited);
}
}
return results;
}
private double backtrackEvaluate(HashMap<String, HashMap<String, Double>> graph, String currNode, String targetNode, double accProduct, Set<String> visited) {
// mark the visit
visited.add(currNode);
double ret = -1.0;
Map<String, Double> neighbors = graph.get(currNode);
if (neighbors.containsKey(targetNode)) {
ret = accProduct * neighbors.get(targetNode);
} else {
for (Map.Entry<String, Double> pair : neighbors.entrySet()) {
String nextNode = pair.getKey();
if (visited.contains(nextNode)) {
continue;
}
ret = backtrackEvaluate(graph, nextNode, targetNode, accProduct * pair.getValue(), visited);
if (ret != -1.0) {
break;
}
}
}
// unmark the visit, for the next backtracking
visited.remove(currNode);
return ret;
}
// Approach 2: My Solution
// Backtracking using a Stack.
public double[] calcEquation2(List<List<String>> equations, double[] values, List<List<String>> queries) {
HashMap<String, HashMap<String, Double>> graph = new HashMap<>();
for(int i=0; i < equations.size() ; i++){
String first = equations.get(i).get(0);
String second = equations.get(i).get(1);
if(!graph.containsKey(first)){
graph.put(first, new HashMap<String, Double>());
}
if(!graph.containsKey(second)){
graph.put(second, new HashMap<String, Double>());
}
graph.get(first).put(second, values[i]);
graph.get(second).put(first, 1/values[i]);
}
double[] answer = new double[queries.size()];
for(int i=0; i<queries.size(); i++){
String start = queries.get(i).get(0);
String end = queries.get(i).get(1);
if(!graph.containsKey(start) || !graph.containsKey(end)){
answer[i] = -1.0;
}
else if(start.equals(end)){
answer[i] = 1.0;
}
else {
answer[i] = dfs(start, end, graph);
}
}
return answer;
}
class Node{
String name;
Double cost;
public Node(String name, Double cost){
this.name = name;
this.cost = cost;
}
}
public double dfs(String start, String end, HashMap<String, HashMap<String, Double>> graph){
double answer = 1.0;
Stack<Node> stack = new Stack<>();
stack.push(new Node(start, 1.0));
Set<String> visited = new HashSet<>();
while(!stack.isEmpty()){
Node current = stack.pop();
String currentName = current.name;
Double currentCost = current.cost;
visited.add(currentName);
if(graph.get(currentName).containsKey(end)){
return currentCost * graph.get(currentName).get(end);
} else {
for(String neighbor : graph.get(currentName).keySet()){
if(!visited.contains(neighbor)){
double cost = currentCost * graph.get(currentName).get(neighbor);
stack.push(new Node(neighbor, cost));
}
}
}
}
return -1.0;
}