leetcode图

leetcode图的相关题目解析:
对图的遍历就是两个经典的方法DFS和BFS。BFS经常用Queue实现,DFS经常用递归实现(可改为栈实现)。

1、Clone Graph
题意:给定图中一个节点,克隆图
思路:用HashMap,key存lable值,value存新clone的node,用BFS方法遍历帮助拷贝neighbors的值。给定给一个节点,若lable在map中有,则表示已经clone;若没有,则将当前节点加入map中,一次clone节点的邻居节点。
代码:

class UndirectedGraphNode {
    int label;
    List<UndirectedGraphNode> neighbors;

    UndirectedGraphNode(int x) {
        label = x;
        neighbors = new ArrayList<UndirectedGraphNode>();
    }
}
    private HashMap<Integer, UndirectedGraphNode> map = new HashMap<>();
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        return clone(node);
    }
    private UndirectedGraphNode clone(UndirectedGraphNode node) {
        if (node == null) return null;
        if (map.containsKey(node.label)) {
            return map.get(node.label);
        }
        UndirectedGraphNode clone = new UndirectedGraphNode(node.label);
        map.put(clone.label, clone);
        for (UndirectedGraphNode neighbor : node.neighbors) {
            clone.neighbors.add(clone(neighbor));
        }
        return clone;
    }

2、Evaluate Division
题意:以算式A / B = k的形式给出若干等式,其中A和B是以字符串表示的变量,k是实数(浮点数)。给定一些查询,返回结果。如果答案不存在,返回 -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 ].

思路:输入等式可以看做一个有向图,例如等式a / b = 2.0,可以转化为两条边:

    public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
        // build graph, use adjacent list
        map = new HashMap();
        for(int i = 0; i < equations.length; i++) {
            String[] equation = equations[i];
            if(!map.containsKey(equation[0])) map.put(equation[0], new ArrayList());
            map.get(equation[0]).add(new Info(equation[1], values[i]));

            if(!map.containsKey(equation[1])) map.put(equation[1], new ArrayList());
            map.get(equation[1]).add(new Info(equation[0], 1 / values[i]));
        }

        double[] result = new double[queries.length];
        for(int i = 0; i < result.length; i++) {
            result[i] = find(queries[i][0], queries[i][1], 1, new HashSet());
        }
        return result;
    }
    HashMap<String, List<Info>> map;

    private double find(String start, String end, double value, Set<String> visited) {
        if(visited.contains(start)) return -1;
        if(!map.containsKey(start)) return -1;

        if(start.equals(end)) return value;
        visited.add(start);
        for(Info next : map.get(start)) {
            double sub = find(next.den, end, value * next.val, visited);
            if(sub != -1.0) return sub;
        }

        visited.remove(start);
        return -1;
    }

    class Info {
        String den;
        double val;
        Info(String den, double val) { this.den = den; this.val = val; }
    }

3、题目:Reconstruct Itinerary
题意:tickets = [[“MUC”, “LHR”], [“JFK”, “MUC”], [“SFO”, “SJC”], [“LHR”, “SFO”]]
Return [“JFK”, “MUC”, “LHR”, “SFO”, “SJC”].

思路:存储在hash中,并且使用PriorityQueue来排序。等我们图建立好了以后,从节点JFK开始遍历,只要当前节点映射的multiset里有节点,我们取出这个节点,将其在multiset里删掉,然后继续递归遍历这个节点,由于题目中限定了一定会有解,那么等图中所有的multiset中都没有节点的时候,我们把当前节点存入结果中(头插形式),然后再一层层回溯回去。

public List<String> findItinerary(String[][] tickets) {
    for (String[] ticket : tickets)
        targets.computeIfAbsent(ticket[0], k -> new PriorityQueue()).add(ticket[1]);
    visit("JFK");
    return route;
}

Map<String, PriorityQueue<String>> targets = new HashMap<>();
List<String> route = new LinkedList();

void visit(String airport) {
    while(targets.containsKey(airport) && !targets.get(airport).isEmpty())
        visit(targets.get(airport).poll());
    route.add(0, airport);
}

4、Minimum Height Trees
题意:给定一系列边,寻找使得树高度最小的根节点
思路:从叶节点开始寻找,寻找离所有叶节点最远的节点,则为根。每次去掉当前图的所有叶子节点,重复此操作直到只剩下最后的根。

    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
    if (n == 1) return Collections.singletonList(0);
    //构建邻接表
    List<Set<Integer>> adj = new ArrayList<>(n);
    for (int i = 0; i < n; ++i) adj.add(new HashSet<>());
    for (int[] edge : edges) {
        adj.get(edge[0]).add(edge[1]);
        adj.get(edge[1]).add(edge[0]);
    }
    //找到所有叶子结点,所有入度(即相连边数)为 1 的节点即是叶子节点。
    List<Integer> leaves = new ArrayList<>();
    for (int i = 0; i < n; ++i)
        if (adj.get(i).size() == 1) leaves.add(i);

    //找高度最小的节点,即找离所有叶子节点最远的节点,也即找最中心的节点。
    while (n > 2) {
        n -= leaves.size();
        List<Integer> newLeaves = new ArrayList<>();
        for (int i : leaves) {
            //每次去掉当前图的所有叶子节点,重复此操作直到只剩下最后的根。
            int j = adj.get(i).iterator().next();
            adj.get(j).remove(i);
            if (adj.get(j).size() == 1) newLeaves.add(j);
        }
        leaves = newLeaves;
    }
    return leaves;   
    }

5、Course Schedule
题意:2, [[1,0],[0,1]],代表有两个课程,0的先行课是1,1的先行课的是0,不可能,输出false。
翻译:在一个有向图中,每次找到一个没有前驱节点的节点(也就是入度为0的节点),然后把它指向其他节点的边都去掉,重复这个过程(BFS),直到所有节点已被找到,或者没有符合条件的节点(如果图中有环存在)。

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[][] matrix = new int[numCourses][numCourses]; // i -> j
        int[] indegree = new int[numCourses];

        for (int i=0; i<prerequisites.length; i++) {
            int ready = prerequisites[i][0];
            int pre = prerequisites[i][1];
            if (matrix[pre][ready] == 0)
                indegree[ready]++; //duplicate case
            matrix[pre][ready] = 1;
        }

        int count = 0;
        Queue<Integer> queue = new LinkedList();
        for (int i=0; i<indegree.length; i++) {
            if (indegree[i] == 0) queue.offer(i);
        }
        while (!queue.isEmpty()) {
            int course = queue.poll();
            count++;
            for (int i=0; i<numCourses; i++) {
                if (matrix[course][i] != 0) {
                    if (--indegree[i] == 0)
                        queue.offer(i);
                }
            }
        }
        return count == numCourses;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值