HomeLeetcode399. Evaluate Division - Leetcode Solutions

399. Evaluate Division – Leetcode Solutions

Description

You are given an array of variable pairs equations and an array of real numbers values, where equations[i] = [Ai, Bi] and values[i] represent the equation Ai / Bi = values[i]. Each Ai or Bi is a string that represents a single variable.

You are also given some queries, where queries[j] = [Cj, Dj] represents the jth query where you must find the answer for Cj / Dj = ?.

Return the answers to all queries. If a single answer cannot be determined, return -1.0.

Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction.

Note: The variables that do not occur in the list of equations are undefined, so the answer cannot be determined for them.

Examples:

Example 1:

Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
Output: [6.00000,0.50000,-1.00000,1.00000,-1.00000]
Explanation: 
Given: a / b = 2.0, b / c = 3.0
queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 
return: [6.0, 0.5, -1.0, 1.0, -1.0 ]
note: x is undefined => -1.0

Example 2:

Input: equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
Output: [3.75000,0.40000,5.00000,0.20000]

Example 3:

Input: equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]]
Output: [0.50000,2.00000,-1.00000,-1.00000]

Solution in Python

Approach:

  1. Build the Graph:
    • Create an adjacency list where each variable is a node.
    • For every equation A / B = k, add an edge from A to B with weight k and from B to A with weight 1/k.
  2. Process Queries:
    • For each query [C, D], perform a DFS starting from C to find D:
      • If either C or D is not in the graph, return -1.0.
      • If C == D, the result is 1.0 (division of any variable by itself).
  3. DFS Implementation:
    • Traverse the graph while keeping track of visited nodes to avoid cycles.
    • Maintain the product of edge weights along the path to calculate the result.
  4. Return Results:
    • Return the results for all queries.
Python
class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
        # Step 1: Build the graph
        graph = {}
        
        for (A, B), value in zip(equations, values):
            if A not in graph:
                graph[A] = {}
            if B not in graph:
                graph[B] = {}
            # Add both forward and backward edges
            graph[A][B] = value
            graph[B][A] = 1 / value
        
        # Step 2: Helper function to perform DFS
        def dfs(current: str, target: str, visited: set, acc_product: float) -> float:
            # If the current node is the target node, return the accumulated product
            if current == target:
                return acc_product
            
            # Mark the current node as visited
            visited.add(current)
            
            # Explore neighbors
            for neighbor, value in graph[current].items():
                if neighbor not in visited:
                    # Recursively call DFS for neighbors
                    result = dfs(neighbor, target, visited, acc_product * value)
                    if result != -1.0:  # If a valid result is found, return it
                        return result
            
            # If no path is found, return -1.0
            return -1.0
        
        # Step 3: Process each query
        results = []
        for C, D in queries:
            if C not in graph or D not in graph:
                results.append(-1.0)  # Either C or D is not in the graph
            elif C == D:
                results.append(1.0)  # C / C = 1.0
            else:
                results.append(dfs(C, D, set(), 1.0))  # Perform DFS to find the result
        
        return results

Explanation of Code:

  1. Graph Construction:
    • The graph is implemented as an adjacency list using a dictionary of dictionaries.
    • Each variable is a key, and its value is another dictionary containing neighbors and edge weights.
  2. DFS:
    • The dfs function takes:
      • current: The current variable.
      • target: The target variable to find.
      • visited: A set to avoid revisiting nodes.
      • acc_product: The accumulated product of edge weights along the path.
    • It recursively explores neighbors and calculates the product until the target is reached or all paths are exhausted.
  3. Query Processing:
    • For each query, we:
      • Check if the variables exist in the graph.
      • Handle special cases like C == D.
      • Perform DFS to compute the result if the variables are connected.
  4. Edge Cases:
    • Variables not in the graph (x / x or a / e when e is undefined).
    • Queries where both variables are the same.

Solution in C++

C++
class Solution {
public:
    // Function to calculate the division results for the queries
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        // Step 1: Create a graph to represent the equations
        unordered_map<string, vector<pair<string, double>>> graph;
        buildGraph(equations, values, graph);

        // Step 2: Process each query and calculate the result
        vector<double> results;
        for (const auto& query : queries) {
            const string& numerator = query[0];
            const string& denominator = query[1];

            // If either variable does not exist in the graph, result is -1.0
            if (graph.find(numerator) == graph.end() || graph.find(denominator) == graph.end()) {
                results.push_back(-1.0);
            } else {
                unordered_set<string> visited;
                double result = -1.0;
                if (numerator == denominator) {
                    result = 1.0; // Same variable division is always 1.0
                } else {
                    result = dfs(graph, numerator, denominator, visited);
                }
                results.push_back(result);
            }
        }

        return results;
    }

private:
    // Function to build the graph from the equations and values
    void buildGraph(vector<vector<string>>& equations, vector<double>& values,
                    unordered_map<string, vector<pair<string, double>>>& graph) {
        for (size_t i = 0; i < equations.size(); ++i) {
            const string& numerator = equations[i][0];
            const string& denominator = equations[i][1];
            double value = values[i];

            // Add edge numerator -> denominator
            graph[numerator].emplace_back(denominator, value);
            // Add edge denominator -> numerator (inverse relation)
            graph[denominator].emplace_back(numerator, 1.0 / value);
        }
    }

    // Depth-First Search to find the value of numerator/denominator
    double dfs(unordered_map<string, vector<pair<string, double>>>& graph,
               const string& current, const string& target,
               unordered_set<string>& visited) {
        if (current == target) return 1.0; // Found target

        visited.insert(current);

        for (const auto& neighbor : graph[current]) {
            const string& nextNode = neighbor.first;
            double value = neighbor.second;

            // Avoid revisiting nodes
            if (visited.find(nextNode) == visited.end()) {
                double result = dfs(graph, nextNode, target, visited);
                if (result != -1.0) { // If a valid path exists, multiply and return
                    return result * value;
                }
            }
        }

        return -1.0; // No valid path found
    }
};

Solution in Javascript

JavaScript
var calcEquation = function(equations, values, queries) {
    // Step 1: Create a graph to represent the equations
    const graph = {};

    // Helper function to add edges to the graph
    const addEdge = (from, to, value) => {
        if (!graph[from]) graph[from] = [];
        graph[from].push([to, value]);
    };

    // Build the graph
    for (let i = 0; i < equations.length; i++) {
        const [a, b] = equations[i];
        const value = values[i];
        addEdge(a, b, value);       // Add edge a -> b with weight value
        addEdge(b, a, 1 / value);  // Add edge b -> a with weight 1 / value
    }

    // Step 2: Solve queries using DFS
    const dfs = (start, end, visited) => {
        // If the node is not in the graph, return -1
        if (!graph[start]) return -1.0;
        // If the start and end are the same, return 1
        if (start === end) return 1.0;

        visited.add(start);

        for (let [neighbor, value] of graph[start]) {
            if (visited.has(neighbor)) continue; // Skip visited nodes
            const result = dfs(neighbor, end, visited);
            if (result !== -1.0) {
                return result * value; // Multiply the current value with the result from neighbor
            }
        }

        return -1.0; // If no path is found, return -1
    };

    // Process each query
    const results = [];
    for (let [c, d] of queries) {
        if (c === d && graph[c]) {
            results.push(1.0); // If querying the same variable, the result is 1
        } else {
            results.push(dfs(c, d, new Set())); // Perform DFS for each query
        }
    }

    return results;
};

Solution in Java

Java
class Solution {
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        // Map to store the graph as adjacency list
        Map<String, Map<String, Double>> graph = new HashMap<>();

        // Step 1: Build the graph
        for (int i = 0; i < equations.size(); i++) {
            String var1 = equations.get(i).get(0);
            String var2 = equations.get(i).get(1);
            double value = values[i];

            // Add the edge var1 -> var2
            graph.putIfAbsent(var1, new HashMap<>());
            graph.get(var1).put(var2, value);

            // Add the edge var2 -> var1 (reciprocal)
            graph.putIfAbsent(var2, new HashMap<>());
            graph.get(var2).put(var1, 1.0 / value);
        }

        // Step 2: Process each query
        double[] results = 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 either variable is not in the graph, return -1.0
            if (!graph.containsKey(start) || !graph.containsKey(end)) {
                results[i] = -1.0;
            } else if (start.equals(end)) {
                // If both variables are the same, the answer is 1.0
                results[i] = 1.0;
            } else {
                // Perform BFS/DFS to find the path from start to end
                Set<String> visited = new HashSet<>();
                results[i] = dfs(graph, start, end, 1.0, visited);
            }
        }

        return results;
    }

    // Helper function to perform DFS
    private double dfs(Map<String, Map<String, Double>> graph, String current, String target, double value, Set<String> visited) {
        // Mark the current node as visited
        visited.add(current);

        // If the target is found, return the accumulated value
        if (graph.get(current).containsKey(target)) {
            return value * graph.get(current).get(target);
        }

        // Explore neighbors
        for (Map.Entry<String, Double> neighbor : graph.get(current).entrySet()) {
            String nextNode = neighbor.getKey();
            double edgeWeight = neighbor.getValue();

            if (!visited.contains(nextNode)) {
                double result = dfs(graph, nextNode, target, value * edgeWeight, visited);
                if (result != -1.0) {
                    return result; // Return valid result if found
                }
            }
        }

        // If no path is found, return -1.0
        return -1.0;
    }
}

Subscribe
Notify of

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Popular