399 Evaluate Division

Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0.


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 ].

The input is: vector> equations, vector& values, vector> queries , where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector.

According to the example above:

equations = [ ["a", "b"], ["b", "c"] ],
values = [2.0, 3.0],
queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].

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

The Idea: In summary, have a/b = k as a link between node a and b, the weight from a to b as k, the reverse link is 1/k. A query follows to the path between two nodes, and the result is the product of the edges within this path. As to why this is, is explained in the graphic below. For my graph, I used a dictionary of sets to represent the relationship between nodes, and an edges map for the look up of the value between two nodes. For finding the path, I used BFS (although DFS would work as well). BFS is able to find the shortest path, which will give it a computational advantage with larger graphs. When the target node is identified, we can backtrack by continously hashing in our prev_array until the start node is reached.

Complexity: O((v+1)*q) time where v is the number of values and q is the number queries and O(n) space.

import collections
import queue

class Solution:
    def calcEquation(self, equations, values, queries):
        :type equations: List[List[str]]
        :type values: List[float]
        :type queries: List[List[str]]
        :rtype: List[float]

        hash_list = collections.defaultdict(set)
        edges_map = {}
        for equation, value in zip(equations, values):
            a = equation[0]
            b = equation[1]

            hash_list[a].add(b)  # main
            edges_map[a + '-' + b] = value

            hash_list[b].add(a)  # reciprocal
            edges_map[b + '-' + a] = 1 / value

        def BFS(start, end):
            q = queue.Queue()

            visited = set()
            prev = {}

            while not q.empty():
                front = q.get()
                if front == end:

                for adj in hash_list[front]:
                    if adj not in visited:
                        prev[adj] = front

            # backtrack
            if end not in prev:
                return float(-1)
            cur_prev = prev[end]
            cur_mult = edges_map[cur_prev + '-' + end]
            while cur_prev != start:
                temp = cur_prev
                cur_prev = prev[temp]
                cur_mult *= edges_map[cur_prev + '-' + temp]
            return cur_mult

        solutions = []
        for a, b in queries:
            if a in hash_list and b in hash_list:
                if a == b:
                    sol = BFS(a, b)

                    # potentially shorter path for future queries
                    edges_map[a + '-' + b] = sol
                    edges_map[b + '-' + a] = 1 / sol

        return solutions

obj = Solution()
equations = [["a","b"],["b","c"],["bc","cd"]]
values = [1.5,2.5,5.0]
queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]]
print(obj.calcEquation(equations, values, queries))

obj2 = Solution()
equations2 = [ ["a", "b"], ["b", "c"] ]
values2 = [2.0, 3.0]
queries2 = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]
print(obj2.calcEquation(equations2, values2, queries2))

obj3 = Solution()
equations3 = [["a","b"],["c","d"]]
values3 = [1.0,1.0]
queries3 = [["a","c"],["b","d"],["b","a"],["d","c"]]
print(obj3.calcEquation(equations3, values3, queries3))

Last updated