BFS solution

"""
main idea: transform a/b=k1, b/c=k2 to a ===(cost:k1 )==> b ==(cost:k2)=> c   for query a/c question, convert it into find the shortest cost from a to c. The cost is k1 * k2
step1: build a two dimensional graph in the form of adjacency list.Dividend is key , divisor and cost are  stored in the form of pairs. In a way divisor acts as dividend's neighber.  
step2: start from dividend with cost of 1.0, traverse all the neighbours of dividend. 
step3: pop out first stop from queue, compare if cur_divisor is same of target_divisor from query.
if target_divisor is same as cur_divisor, means the traversal starting from target_dividend has found divisor;
if not found, push current new divisor and cost so far into queue.
step4: Each time a divisor is visited, save it in the set to prevent repeated multiplication.
step5: visited all the neighbours of possible divisor on the way of traversal, updating queue  until queue is empty. it means target divisor is not found. return -1.0
"""
class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
        # time O(m(p+n)) ~ O(N^2) where m : query #, p: equation #, n: val #
        # e.g. x/y = val  
        graph = {}
        def build_graph(equations,values):
            def add_edge(x,y,val):
                if x not in graph:
                    graph[x] = [(y,val)]
                else:
                    graph[x].append((y,val))
            for (x,y),val in zip(equations,values):
                add_edge(x,y,val)
                add_edge(y,x,1/val)

        def find_path(query):
            # a/ b
            a, b = query
            if a not in graph or b not in graph:
                return -1.0
            
            q = collections.deque([(a,1.0)])
            seen = set()
            
            while q:
                y, val_cur = q.popleft()
                # a/a = 1.0
                if b == y:
                    return val_cur
                # check all nodes connected  with x 
                seen.add(y)
                for y_neighbour,val in graph[y]:
                    if y_neighbour not in seen:
                        q.append((y_neighbour,val*val_cur))
            return -1.0
        
        build_graph(equations,values)
        return [find_path(q) for q in queries]                         

Floyd-Warshall solution

class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
        # time O(V^3)
        quot = collections.defaultdict(dict)
        for (num,dev),val in zip(equations,values):
            quot[num][num] = quot[dev][dev] = 1.0
            quot[num][dev] = val
            quot[dev][num] = 1/val
        for k in quot:
            for i in quot[k]:
                for j in quot[k]:
                    quot[i][j] = quot[i][k]*quot[k][j]
        return [quot[num].get(dev,-1.0) for num,dev in queries]