5.leetcode--图

1.所有可能的路径

题目要求:

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序

graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

示例 1:

输入:graph = [[1,2],[3],[3],[]]

输出:[[0,1,3],[0,2,3]]

解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3

难度:🌟🌟

解答:图的遍历框架

// 记录被遍历过的节点
boolean[] visited;
// 记录从起点到当前节点的路径
boolean[] onPath;

/* 图遍历框架 */
void traverse(Graph graph, int s) {
    if (visited[s]) return;
    // 经过节点 s,标记为已遍历
    visited[s] = true;
    // 做选择:标记节点 s 在路径上
    onPath[s] = true;
    for (int neighbor : graph.neighbors(s)) {
        traverse(graph, neighbor);
    }
    // 撤销选择:节点 s 离开路径
    onPath[s] = false;
}
class Solution {
    List<List<Integer>> res = new LinkedList();

    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        LinkedList<Integer> path = new LinkedList();
        traverse(graph, 0, path);
        return res;
    }

    public void traverse(int[][] graph, int s, LinkedList<Integer> path){
        path.add(s);
        int n =graph.length;
        if(s == n-1){ //到达终点 记录路径 结束
            res.add(new LinkedList<>(path));
        }
        for(int v:graph[s]){ //遍历相邻节点
            traverse(graph,v,path);
        }
        //s不能到达终点 
        path.removeLast();
        return;
    }
}

2.课程表

题目要求:

你这个学期必须选修 numCourses 门课程,记为 0numCourses - 1

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

  • 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]

输出:true

解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

难度:🌟🌟

解答:

  • 构造图+环检测 // 记录一次递归堆栈中的节点 boolean[] onPath;

// 记录遍历过的节点,防止走回头路 boolean[] visited;

class Solution {
    // 记录一次递归堆栈中的节点
    boolean[] onPath;
    // 记录遍历过的节点,防止走回头路
    boolean[] visited;
    // 记录图中是否有环
    boolean hasCycle = false;

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<Integer>[] graph = buildGraph(numCourses, prerequisites);
        visited = new boolean[numCourses];
        onPath = new boolean[numCourses];

        for (int i = 0; i < numCourses; i++) {
            // 遍历图中的所有节点
            traverse(graph, i);
        }
        // 只要没有循环依赖可以完成所有课程
        return !hasCycle;

    }

    public List<Integer>[] buildGraph(int numCourses, int[][] prerequisites){
        List<Integer>[] graph = new LinkedList[numCourses];
        // 图中共有 numCourses 个节点
        for (int i=0;i<numCourses;i++){
            graph[i] = new LinkedList<>();
        }
        for (int edge[]:prerequisites){
            int from = edge[1];
            int to = edge[0];
            graph[from].add(to);
        }
        return  graph;
    }

    public void traverse(List<Integer>[] graph, int s){
        if(onPath[s]){
            hasCycle = true; //本次路径出现环
        }
        if(visited[s] || hasCycle){
            // 如果已经找到了环,也不用再遍历了
            return;
        }
        visited[s] = true;
        onPath[s] = true;
        for (int t:graph[s]) {
            traverse(graph,t);
        }
        onPath[s] = false;
    }
}
  • 拓扑排序 将后序遍历的结果进行反转,就是拓扑排序的结果

3.可能的二分法 二分图 双色问题

题目要求:

给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。

给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和 bi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false

示例 1:

输入:n = 4, dislikes = [[1,2],[1,3],[2,4]]

输出:true

解释:group1 [1,4], group2 [2,3]

难度:🌟🌟

解答:

  • 互相讨厌的关系看左边---- 构建无向图

  • 相邻节点不同组----染双色色----遍历

private boolean ok = true;
    private boolean[] color;
    private boolean[] visited;

    public boolean possibleBipartition(int n, int[][] dislikes) {
        // 图节点编号从 1 开始
        color = new boolean[n + 1];
        visited = new boolean[n + 1];
        // 转化成邻接表表示图结构
        List<Integer>[] graph = buildGraph(n, dislikes);

        for (int v = 1; v <= n; v++) {
            if (!visited[v]) {
                traverse(graph, v);
            }
        }

        return ok;
    }

    // 和之前的 traverse 函数完全相同
    private void traverse(List<Integer>[] graph, int v){
        if (!ok) {
            return;
        }
        visited[v] = true;
        for (int current : graph[v]){
            if(!visited[current]){ //没遍历过
                color[current]=!color[v]; //反响染色
                traverse(graph,current);
            }else {
                if (color[current] == color[v]){
                    ok = false;
                    return;
                }
            }
        }
        return;
    }
    

    // 建图函数
    private List<Integer>[] buildGraph(int n, int[][] dislikes){
        List<Integer>[] graph = new LinkedList[n+1];
        for (int i = 1; i <= n; i++) {
            graph[i] = new LinkedList<>();
        }

        for (int[] edge:dislikes) {
            int w = edge[0];
            int m = edge[1];
            //无向图」相当于「双向图」
            graph[w].add(m);
            graph[m].add(w);
        }
        return graph;
    }

4.网络延迟时间 Dijkstra

题目要求:

n 个网络节点,标记为 1n

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1

示例 1:

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2

输出:2

难度:🌟🌟

解答:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值