LeetCode399 Evaluate Division

399 Evaluate Division: this question seems pretty odd for being catagorized in graph. so this problem is basically given a set of equations, and given a set of queries, return the query results in array.
Example:
equations = [ [“a”, “b”], [“b”, “c”] ],
values = [2.0, 3.0],
queries = [ [“a”, “c”], [“b”, “a”], [“a”, “e”], [“a”, “a”], [“x”, “x”] ].

return [6.0, 0.5, -1.0, 1.0, -1.0 ].

if I wasn’t been told that the problem can be solve in graph, I won’t think of that, but now I’ve been told, so it seems that all those equation variable can be seen as nodes. and the values can be seen as the weight of edge(not that same though). and the graph is clearly a directed graph.
and in concise: Image a/b = k as a link between node a and b, the weight from a to b is k, the reverse link is 1/k. Query is to find a path between two nodes.

//Graph related problems: Image a/b = k as a link between node a and b, the weight from a to b is k, the reverse link is 1/k. Query is to find a path between two nodes. so it can be seen as an undirected graph, but the weight from a to b and the wight from b to a is not the same. so we need to store every node as key in hashmap graph.
class Solution {
    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        HashMap<String, ArrayList<String>> pairs = new HashMap<String, ArrayList<String>>(); //store every node and its linked node
        HashMap<String, ArrayList<Double>> valuesPair = new HashMap<String, ArrayList<Double>>(); //store every node and its weighted edges value
        for (int i = 0; i < equations.size(); i++) { //
            List<String> equation = equations.get(i);
            if (!pairs.containsKey(equation.get(0))) { //initialize every node as key in pairs and valuesPair with an empty ArrayList
                pairs.put(equation.get(0), new ArrayList<String>());
                valuesPair.put(equation.get(0), new ArrayList<Double>());
            }
            if (!pairs.containsKey(equation.get(1))) { //the same as previous if
                pairs.put(equation.get(1), new ArrayList<String>());
                valuesPair.put(equation.get(1), new ArrayList<Double>());
            }
            pairs.get(equation.get(0)).add(equation.get(1));
            pairs.get(equation.get(1)).add(equation.get(0));
            valuesPair.get(equation.get(0)).add(values[i]);
            valuesPair.get(equation.get(1)).add(1/values[i]); //and then construct node pair and its corresponding weights
        }
        
        double[] result = new double[queries.size()];
        for (int i = 0; i < queries.size(); i++) {
            List<String> query = queries.get(i); //for each query
            result[i] = dfs(query.get(0), query.get(1), pairs, valuesPair, new HashSet<String>(), 1.0); //dfs(query_start, query_end, GraphHashmap1, GraphHashMap2, hashset, initial_result)
            if (result[i] == 0.0) result[i] = -1.0; //if the results returns 0, then return -1 instead, shows we don;t have a valid result
        }
        return result;
    }
    
    //use dfs to backtracking a valid way start from start and end to end
    private double dfs(String start, String end, HashMap<String, ArrayList<String>> pairs, HashMap<String, ArrayList<Double>> values, HashSet<String> set, double value) {
        if (set.contains(start)) return 0.0; //if we have already encountered this bsfore, it means we have a cycle, that means this way is dead, and we have to try another way using backtracking, but how do we know that 
        if (!pairs.containsKey(start)) return 0.0; //if we didn't even contains this node, like we only have a/b=2, but the query is a/e
        if (start.equals(end)) return value; //start node go all the way dfs, till it arrives the end node, and return tha value
        
        set.add(start);
        ArrayList<String> strList = pairs.get(start); //strList is every node that current node starts
        ArrayList<Double> valueList = values.get(start); //valueList is the weight starting from current node
        double tmp = 0.0;
        for (int i = 0; i < strList.size(); i++) {
            tmp = dfs(strList.get(i), end, pairs, values, set, value*valueList.get(i)); //
            if (tmp != 0.0) { //if this way is not dead, and go all the way, and returns a value not 0, then we can just break
                break;
            }
        }
        set.remove(start); //backtracking 
        return tmp;
    }
}

even though the code seems a bit of long, but the general idea is pretty simple: first, as usual, constrcut the graph, keep in mind that we need to use two hashmap to represent this wieighted undirected graph. one uses for store<nodes, list of all its linked nodes> and another is to store<node, list of all the weight of its edges>
and for every query we uses dfs with backtracking to get us a valid way from start node to end node. there are many details in that, for instance, we need to take care of the cycle before we find the end node. and how should we do if we find a valid path and how do we know it is valid or not? what parameters should be included in the dfs function?(start and end, two graph hashmaps, and a set(uses for circle detect), and the results)
Many details, please be careful.

and there is a second way to do this problem, Union find:

think about this: how to combine union find idea to solve our problem? well, if two nums have the same root, we can get the result.
the following is the code of union find. you can see the input is not the same as previous, but they are the same meaning

class Solution {
    public double[] calcEquation(String[][] e, double[] values, String[][] q) {
        double[] res = new double[q.length];
        Map<String, String> root = new HashMap<>();
        Map<String, Double> dist = new HashMap<>();
        for (int i = 0; i < e.length; i++) {
            String r1 = find(root, dist, e[i][0]);
            String r2 = find(root, dist, e[i][1]);
            root.put(r1, r2);
            dist.put(r1, dist.get(e[i][1]) * values[i] / dist.get(e[i][0]));
        }
        for (int i = 0; i < q.length; i++) {
            if (!root.containsKey(q[i][0]) || !root.containsKey(q[i][1])) {
                res[i] = -1.0;
                continue;
            }
            String r1 = find(root, dist, q[i][0]);
            String r2 = find(root, dist, q[i][1]);
            if (!r1.equals(r2)) {
                res[i] = -1.0;
                continue;
            }
            res[i] = (double) dist.get(q[i][0]) / dist.get(q[i][1]);
        }
        return res;
    }
    
    private String find(Map<String, String> root, Map<String, Double> dist, String s) { //find the root of string s
        if (!root.containsKey(s)) {
            root.put(s, s);
            dist.put(s, 1.0);
            return s;
        }
        if (root.get(s).equals(s)) return s;
        String lastP = root.get(s);
        String p = find(root, dist, lastP);
        root.put(s, p);
        dist.put(s, dist.get(s) * dist.get(lastP));
        return p;
    }
}
posted @ 2020-05-10 11:11  EvanMeetTheWorld  阅读(20)  评论(0)    收藏  举报