图笔记

图笔记

图的遍历代码结构和回溯代码结构相似,因此在整理回溯题目之后整理了图相关的题目,这部分的题目考察较少,不建议花太多工夫,了解常用的算法即可,力扣周赛后两题极高概率考

Graph graph;
boolean[] visited;

/* 图遍历框架 */
void traverse(Graph graph, int s) {
    if (visited[s]) return;
    // 经过节点 s
    visited[s] = true;
    for (TreeNode neighbor : graph.neighbors(s))
        traverse(neighbor);
    // 离开节点 s
    visited[s] = false;   
}

可以看到,图的遍历框架中,visited[]用于防止图走环,无环图不需要visited[]

在回溯代码框架中,visited[]更新是放在for循环中的,更关注树枝


图的学习过程,应当关注两点

  1. 构建图(buildGraph,必须写熟)
  2. 图遍历

💚 -- Easy

💛 -- Medium

🧡 -- Hard

题目顺序,codetop.cc 默认排序规则(题目少,出现的频次很少)


💛207. 课程表 --->环检测(使用visited[] 和 onPath[])

拓扑排序联想场景:课程依赖

按照二叉树去联想,先完成上层的依赖课程,才能学习下层的课程

💛210. 课程表 II--->拓扑排序(后序遍历的结果进行反转,就是拓扑排序的结果,本题需要综合环判断)

💛797. 所有可能的路径--->图遍历


Dijkstra要求不能存在负权重边,从起点start到所有其他节点的最短路径都算出来。

需要辅助类 class State{int id; int distFromStart},优先队列PQ(State.distFromStart升序)

根据 BFS 的逻辑和无权图的特点,第一次遇到某个节点所走的步数就是最短距离

第一次经过某个节点时的路径权重,不见得就是最小的

因为优先级队列自动排序的性质,每次从队列里面拿出来的都是distFromStart值最小的,所以当你从队头拿出一个节点,如果发现这个节点就是终点end,那么distFromStart对应的值就是从startend的最短距离。

💛743. 网络延迟时间--->Dijkstra

💛1631. 最小体力消耗路径--->Dijkstra

💛1514. 概率最大的路径--->Dijkstra


并集查询Union-Find 解决图论中「动态连通性」问题的。

优秀题解,含UF概念

💛2316. 统计无向图中无法互相到达点对数-->UF

💛130. 被围绕的区域-> UF,BFS

X💛128. 最长连续序列--->该题与最长子序列不同,可以使用HashSet、UF(不建议用)


二分图判定核心在于使用DFS和BFS遍历,使用两种颜色将所有节点染色,且相邻节点的颜色不同,DFS更为常用

💛785. 判断二分图

💛886. 可能的二分法


生成树:含有图中所有顶点的「无环连通子图

最小生成树:所有可能的生成树中,权重和最小的那棵

一般来说,在无向加权图中计算最小生成树

使用贪心算法保证最小权重,按边权重排序,优先使用最小权重的可用边

Kruskal 最小生成树算法,关键是要熟悉并查集算法(UF + 贪心)

💛261. 以图判树

给定编号从 0n - 1n 个结点。给定一个整数 n 和一个 edges 列表,其中 edges[i] = [ai, bi] 表示图中节点 aibi 之间存在一条无向边。

如果这些边能够形成一个合法有效的树结构,则返回 true ,否则返回 false

image-20230224152000455

输入: n = 5, edges = [[0,1],[0,2],[0,3],[1,4]]
输出: true

image-20230224152014271

输入: n = 5, edges = [[0,1],[1,2],[2,3],[1,3],[1,4]]
输出: false

函数标签

class Solution {
    public boolean validTree(int n, int[][] edges) {}
}

💛1135. 最低成本联通所有城市

想象一下你是个城市基建规划者,地图上有 n 座城市,它们按以 1n 的次序编号。

给你整数 n 和一个数组 conections,其中 connections[i] = [xi, yi, costi] 表示将城市 xi 和城市 yi 连接所要的costi连接是双向的)。

返回连接所有城市的最低成本,每对城市之间至少有一条路径。如果无法连接所有 n 个城市,返回 -1

最小成本 应该是所用全部连接成本的总和。

image-20230224152107942

输入:n = 3, conections = [[1,2,5],[1,3,6],[2,3,1]]
输出:6
解释:选出任意 2 条边都可以连接所有城市,我们从中选取成本最小的 2 条。

image-20230224152116428

输入:n = 4, conections = [[1,2,3],[3,4,4]]
输出:-1
解释:即使连通所有的边,也无法连接所有城市。

函数标签

class Solution {
    public int minimumCost(int n, int[][] connections) {}
}

💛1584. 连接所有点的最小费用--->注意记录点之间距离的技巧int[] {i, j, cost}

Prim 算法 利用「切分定理」的贪心思想,使生成树的权重尽可能小,不需要事先排序

切分定理+BFS+visited)

「切分定理」:对于任意一种「切分」,其中权重最小的那条「横切边」一定是构成最小生成树的一条边

不断的将相邻节点加入横切边的队伍中,依据 cut({a,b,c}) = cut({a,b}) + cut({c})

BFS中,使用PriorityQueue不断将最小权边取出,添加到生成树中

适用的题目与Kruskal是一样的

需要实现的方法

class Prim {
    // 核心
    PriorityQueue<int[]> pq;
    int weight;
    boolean[] inTree;
    public Prim(int n, ArrayList<int[]>[] graph) {}
    void cut(int node) {}
    boolean isConnected() {}
    int weight() {}
}

名流算法

1、所有其他人都认识「名人」。

2、「名人」不认识任何其他人。

💛277. 搜寻名人

假设你是一个专业的狗仔,参加了一个 n 人派对,其中每个人被从 0n - 1 标号。在这个派对人群当中可能存在一位 “名人”。所谓 “名人” 的定义是:其他所有 n - 1 个人都认识他/她,而他/她并不认识其他任何人。

现在你想要确认这个 “名人” 是谁,或者确定这里没有 “名人”。而你唯一能做的就是问诸如 “A 你好呀,请问你认不认识 B呀?” 的问题,以确定 A 是否认识 B。你需要在(渐近意义上)尽可能少的问题内来确定这位 “名人” 是谁(或者确定这里没有 “名人”)。

在本题中,你可以使用辅助函数 bool knows(a, b) 获取到 A 是否认识 B。请你来实现一个函数 int findCelebrity(n)

派对最多只会有一个 “名人” 参加。若 “名人” 存在,请返回他/她的编号;若 “名人” 不存在,请返回 -1

image-20230224191034835

输入: graph = [
  [1,1,0],
  [0,1,0],
  [1,1,1]
]
输出: 1
解释: 有编号分别为 0、1 和 2 的三个人。graph[i][j] = 1 代表编号为 i 的人认识编号为 j 的人,而 graph[i][j] = 0 则代表编号为 i 的人不认识编号为 j 的人。“名人” 是编号 1 的人,因为 0 和 2 均认识他/她,但 1 不认识任何人。

image-20230224191044618

输入: graph = [
  [1,0,1],
  [1,1,0],
  [0,1,1]
]
输出: -1
解释: 没有 “名人”

函数标签

/* The knows API is defined in the parent class Relation.
      boolean knows(int a, int b); */

public class Solution extends Relation {
    public int findCelebrity(int n) {}
}
posted @ 2023-02-24 19:14  jentreywang  阅读(12)  评论(0编辑  收藏  举报