【BFS】《单源、多源 BFS:图搜索算法的双生力量》
文章目录
- 前言
- 单源BFS例题
- 一、迷宫中离入口最近的出口
- 二、 最小基因变化
- 三、单词接龙
- 四、为高尔夫比赛砍树
- 多源BFS例题
- 一、 01 矩阵
- 二、飞地的数量
- 三、地图中的最高点
- 四、地图分析
- 结语
前言
什么是单源、多源BFS算法问题呢?
BFS(Breadth - First Search)即广度优先搜索算法,单源和多源 BFS 算法问题主要用于解决图或网格中的最短路径问题 ,二者具体介绍如下:
单源 BFS 算法问题:指从图中的一个起始节点(源点)出发,按照广度优先的策略,逐层向外扩展访问其他节点,找到从该源点到图中其他各个可达节点的最短路径。例如在一个迷宫网格中,从一个特定入口出发找出口,就可以使用单源 BFS。该算法适用于无权图或所有边权值都相同的图。它利用队列来存储待访问节点,先将源点入队,然后不断取出队首节点并访问其相邻未访问节点,将这些相邻节点入队,直到队列为空或找到目标节点。
多源 BFS 算法问题:是有多个起始节点(源点),目标是找到从这些多个源点到图中其他各个可达节点的最短路径。例如在一个城市地图中,有多个垃圾回收站(源点),要计算各个小区到最近垃圾回收站的距离,就可能用到多源 BFS。多源 BFS 的核心在于初始化队列时将所有源点都放入队列,之后和单源 BFS 类似,按广度优先顺序访问节点并扩展,直到遍历完所有可达节点。它也主要用于边权为 1 或边权相等的情况 。
解决多源 BFS 问题时,若直接转化为若干个单源最短路问题求解,可能因重复查找过多而超时。更好的方法是把所有源点视为一个 “超级源点” ,将问题转化为单源最短路问题来处理。
下面,本片文章将通过例题为大家详细介绍单源、多源BFS算法!
单源BFS例题
一、迷宫中离入口最近的出口
- 题目链接:迷宫中离入口最近的出口
- 题目描述:
给你⼀个 m x n 的迷宫矩阵 maze (下标从 0 开始),矩阵中有空格⼦(⽤ ‘.’ 表示)和墙(⽤ ‘+’ 表示)。同时给你迷宫的⼊⼝ entrance ,用 entrance = [entrancerow, entrancecol] 表示你⼀开始所在格子的行和列。 每⼀步操作,你可以往上,下,左或者右移动⼀个格子。你不能进⼊墙所在的格子,你也不能离开迷宫。你的目标是找到离 entrance 最近 的出口。出口的含义是 maze 边界上的空格子。
entrance 格子不算出口。 请你返回从 entrance 到最近出⼝的最短路径的步数 ,如果不存在这样的路径,请你返回 -1 。
⽰例 1:
输⼊:maze = [[“+”,“+”,“.”,“+”],[“.”,“.”,“.”,“+”],[“+”,“+”,“+”,“.”]], entrance = [1,2]
输出:1
解释:总共有 3 个出⼝,分别位于 (1,0),(0,2) 和 (2,3) 。 ⼀开始,你在⼊⼝格⼦ (1,2) 处。
◦ 你可以往左移动 2 步到达 (1,0) 。
◦ 你可以往上移动 1 步到达 (0,2) 。 从入口处没法到达 (2,3) 。
所以,最近的出口是 (0,2) ,距离为 1 步。
⽰例 2:
输⼊:maze = [[“+”,“+”,“+”],[“.”,“.”,“.”],[“+”,“+”,“+”]], entrance = [1,0]
输出:2
解释:迷宫中只有 1 个出口,在 (1,2) 处。(1,0) 不算出口,因为它是入口格⼦。 初始时,你在入口与格⼦ (1,0) 处。
◦ 你可以往右移动 2 步到达 (1,2) 处。 所以,最近的出口为 (1,2) ,距离为 2 步。
⽰例 3:
输⼊:maze = [[“.”,“+”]], entrance = [0,0]
输出:-1
解释:这个迷宫中没有出⼝。
提示:
maze.length == m
maze[i].length == n
1 <= m, n <= 100
maze[i][j] 要么是 ‘.’ ,要么是 ‘+’ 。
entrance.length == 2
0 <= entrancerow < m
0 <= entrancecol < n
entrance ⼀定是空格子。
-
解法(bfs 求最短路):
算法思路: 利⽤层序遍历来解决迷宫问题,是最经典的做法。 我们可以从起点开始层序遍历,并且在遍历的过程中记录当前遍历的层数。这样就能在找到出口的时候,得到起点到出口的最短距离。 -
代码示例:
int[] dx = {0, 0, -1, 1};int[] dy = {1, -1, 0, 0};public int nearestExit(char[][] maze, int[] e) {int m = maze.length, n = maze[0].length;boolean[][] vis = new boolean[200][200];Queue<int[]> q = new LinkedList<>();q.add(new int[]{e[0], e[1]});vis[e[0]][e[1]] = true;int step = 0;while (!q.isEmpty()) {step++;int sz = q.size();for (int i = 0; i < sz; i++) {int[] t = q.poll();int a = t[0], b = t[1];for (int j = 0; j < 4; j++) {int x = a + dx[j], y = b + dy[j];if (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == '.' && !vis[x][y]) {if (x == 0 || x == m - 1 || y == 0 || y == n - 1) return step;vis[x][y] = true;q.add(new int[]{x, y});}}}}return -1;}
二、 最小基因变化
- 题目链接:最小基因变化
- 题目描述:
基因序列可以表示为⼀条由 8 个字符组成的字符串,其中每个字符都是 ‘A’ 、 ‘C’ 、 ‘G’ 和’T’ 之⼀。 假设我们需要调查从基因序列 start 变为 end 所发⽣的基因变化。⼀次基因变化就意味着这个 基因序列中的⼀个字符发⽣了变化。
• 例如, “AACCGGTT” --> “AACCGGTA” 就是⼀次基因变化。 另有⼀个基因库 bank 记录了所有有效的基因变化,只有基因库中的基因才是有效的基因序列。 (变化后的基因必须位于基因库 bank 中) 给你两个基因序列 start 和 end ,以及⼀个基因库 bank ,请你找出并返回能够使 start变化为 end 所需的最少变化次数。如果无法完成此基因变化,返回 -1 。
注意:起始基因序列 start 默认是有效的,但是它并不⼀定会出现在基因库中
示例 1:
输入:start = “AACCGGTT”, end = “AACCGGTA”, bank = [“AACCGGTA”]
输出:1
⽰例 2:
输⼊:start = “AACCGGTT”, end = “AAACGGTA”, bank =
[“AACCGGTA”,“AACCGCTA”,“AAACGGTA”]
输出:2
⽰例 3:
输入:start = “AAAAACCC”, end = “AACCCCCC”, bank =[“AAAACCCC”,“AAACCCCC”,“AACCCCCC”]
输出:3
提示:
◦ start.length == 8
◦ end.length == 8
◦ 0 <= bank.length <= 10
◦ bank[i].length == 8
◦ start 、 end 和 bank[i] 仅由字符 [‘A’, ‘C’, ‘G’, ‘T’] 组成
-
解法:
算法思路: 如果将「每次字符串的变换」抽象成图中的「两个顶点和⼀条边」的话,问题就变成了「边权为 1 的最短路问题」。
因此,从起始的字符串开始,来⼀次 bfs 即可。 -
代码示例:
int[] dx = {0, 0, -1, 1};int[] dy = {1, -1, 0, 0};public int minMutation(String startGene, String endGene, String[] bank) {Set<String> vis = new HashSet<>();//用来标记已经搜索过的状态Set<String> hash = new HashSet<>();//用来统计基因库里的字符串for (String s : bank) {hash.add(s);}char[] charge = {'A', 'C', 'G', 'T'};if (startGene.equals(endGene)) {return 0;}if (!hash.contains(endGene)) return -1;Queue<String> q = new LinkedList<>();q.add(startGene);vis.add(startGene);int step = 0;while (!q.isEmpty()) {step++;int sz = q.size();while (sz-- != 0) {String t = q.poll();for (int i = 0; i < 8; i++) {char[] tmp = t.toCharArray();for (int j = 0; j < 4; j++) {tmp[i] = charge[j];String next = new String(tmp);if (hash.contains(next) && !vis.contains(next)) {if (next.equals(endGene)) return step;q.add(next);vis.add(next);}}}}}return -1;}
三、单词接龙
- 题目链接:单词接龙
- 题目描述:
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是⼀个按下述规格形成的序 列 beginWord -> s(1) -> s(2) -> … -> s(k) :
• 每⼀对相邻的单词只差⼀个字⺟。 • 对于 1 <= i <= k 时,每个 s(i) 都在 wordList 中。注意, beginWord 不需要 在 wordList 中。
• s(k) == endWord 给你两个单词 beginWord 和 endWord 和⼀个字典 wordList ,返回 从 beginWord 到endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。
⽰例 1:
输⼊:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:5
解释:⼀个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”, 返回它的长度 5。
⽰例 2:
输⼊:beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:0
解释:endWord “cog” 不在字典中,所以无法进行转换。
提示:
◦ 1 <= beginWord.length <= 10
◦ endWord.length == beginWord.length
◦ 1 <= wordList.length <= 5000
◦ wordList[i].length == beginWord.length
◦ beginWord 、 endWord 和 wordList[i] 由⼩写英⽂字⺟组成 ◦ beginWord != endWord
◦ wordList 中的所有字符串互不相同
- 解法:
算法思路:本题的算法思路与上题相似,大家可以自行尝试编写代码提高代码能力。 - 代码示例:
int[] dx = {0, 0, -1, 1};
int[] dy = {1, -1, 0, 0};
public int ladderLength(String beginWord, String endWord, List<String> wordList) {Set<String> hash = new HashSet<>();for (String s : wordList) hash.add(s);Set<String> vis = new HashSet<>();if (!hash.contains(endWord)) return 0;Queue<String> q = new LinkedList<>();vis.add(beginWord);q.add(beginWord);int ret = 1;while (!q.isEmpty()) {ret++;int size = q.size();while (size-- != 0) {String t = q.poll();for (int i = 0; i < t.length(); i++) {char[] tmp = t.toCharArray();for (char ch = 'a'; ch <= 'z'; ch++) {tmp[i] = ch;String next = new String(tmp);if (hash.contains(next) && !vis.contains(next)) {if (next.equals(endWord)) return ret;q.add(next);vis.add(next);}}}}}return 0;}
四、为高尔夫比赛砍树
- 题目链接:为高尔夫比赛砍树
- 题目描述:
你被请来给⼀个要举办高尔夫比赛的树林砍树。树林由⼀个 m x n 的矩阵表示, 在这个矩阵中:
◦ 0 表⽰障碍,无法触碰
◦ 1 表示地⾯,可以行走
◦ ⽐ 1 ⼤的数 表⽰有树的单元格,可以行走,数值表⽰树的⾼度
每⼀步,你都可以向上、下、左、右四个⽅向之⼀移动⼀个单位,如果你站的地方有⼀棵树,那么你 可以决定是否要砍倒它。 你需要按照树的高度从低向⾼砍掉所有的树,每砍过⼀颗树,该单元格的值变为 1 (即变为地面)。你将从 (0, 0) 点开始工作,返回你砍完所有树需要走的最小步数。 如果你⽆法砍完所有的树,返回 -1 。 可以保证的是,没有两棵树的⾼度是相同的,并且你至少需要砍倒⼀棵树。
⽰例 1:
输⼊:forest = [[1,2,3],[0,0,4],[7,6,5]]
输出:6
解释:沿着上⾯的路径,你可以⽤ 6 步,按从最矮到最⾼的顺序砍掉这些树。
⽰例 2:
输⼊:forest = [[1,2,3],[0,0,0],[7,6,5]]
输出:-1
解释:由于中间⼀⾏被障碍阻塞,⽆法访问最下⾯⼀⾏中的树。
示例 3:
输入:forest = [[2,3,4],[0,0,5],[8,7,6]]
输出:6
解释:可以按与⽰例 1 相同的路径来砍掉所有的树
提⽰:
◦ m == forest.length
◦ n == forest[i].length
◦ 1 <= m, n <= 50
◦ 0 <= forest[i][j] <= 10^9
- 解法:
a. 先找出砍树的顺序;
b. 然后按照砍树的顺序,⼀个⼀个的⽤ bfs 求出最短路即可 - 代码示例:
int m, n;int[] dx = {0, 0, 1, -1};int[] dy = {1, -1, 0, 0};public int cutOffTree(List<List<Integer>> f) {m = f.size();n = f.get(0).size();//准备工作:确定看书顺序List<int[]> trees = new ArrayList<>();for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (f.get(i).get(j) > 1) {trees.add(new int[]{i, j});}}}Collections.sort(trees, (a, b) -> {return f.get(a[0]).get(a[1]) - f.get(b[0]).get(b[1]);});//按照顺序砍树int ret = 0;int bx = 0, by = 0;for (int[] tree : trees) {int x = tree[0], y = tree[1];int step = bfs(f, bx, by, x, y);if (step == -1) return -1;ret += step;bx = x;by = y;}return ret;}public int bfs(List<List<Integer>> f, int bx, int by, int ex, int ey) {if (bx == ex && by == ey) {return 0;}Queue<int[]> q = new LinkedList<>();boolean[][] vis = new boolean[m][n];q.add(new int[]{bx, by});vis[bx][by] = true;int step = 0;while (!q.isEmpty()) {step++;int sz = q.size();while (sz-- != 0) {int[] t = q.poll();int a = t[0], b = t[1];for (int i = 0; i < 4; i++) {int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && f.get(x).get(y) != 0 && !vis[x][y]) {if (x == ex && y == ey) {return step;}vis[x][y] = true;q.add(new int[]{x, y});}}}}return -1;}
多源BFS例题
一、 01 矩阵
- 题目链接:01矩阵
- 题目描述:
给定⼀个由 0 和 1 组成的矩阵 mat ,请输出⼀个⼤⼩相同的矩阵,其中每⼀个格子是mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1:
输⼊:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
示例 2:
输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]
提⽰:
m == mat.length
n == mat[i].length
1 <= m, n <= 104
1 <= m * n <= 104
mat[i][j] is either 0 or 1.
mat 中⾄少有⼀个 0
- 解法(bfs)(多个源头的最短路问题)
算法思路: 对于求的最终结果,我们有两种方式:
• 第⼀种⽅式:从每⼀个 1 开始,然后通过层序遍历找到离它最近的 0 。 这⼀种⽅式,我们会以所有的 1 起点,来⼀次层序遍历,势必会遍历到很多重复的点。并且如果 矩阵中只有⼀个 0 的话,每⼀次层序遍历都要遍历很多层,时间复杂度较⾼。
• 换⼀种⽅式:从 0 开始层序遍历,并且记录遍历的层数。当第⼀次碰到 1 的时候,当前的层数 就是这个 1 离 0 的最短距离。 这⼀种方式,我们在遍历的时候标记⼀下处理过的 1 ,能够做到只⽤遍历整个矩阵⼀次,就能得 到最终结果。
但是,这里有⼀个问题, 0 是有很多个的,我们怎么才能保证遇到的 1 距离这⼀个 0 是最近的呢?
其实很简单,我们可以先把所有的 0 都放在队列中,把它们当成⼀个整体,每次把当前队列⾥⾯的所 有元素向外扩展⼀次。 - 代码示例:
int[] dx = {0, 0, 1, -1};int[] dy = {1, -1, 0, 0};public int[][] updateMatrix(int[][] mat) {int m = mat.length;int n = mat[0].length;int[][] dist = new int[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {dist[i][j] = -1;}}Queue<int[]> q = new LinkedList<>();for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (mat[i][j] == 0) {dist[i][j] = 0;q.add(new int[]{i, j});}}}while (!q.isEmpty()) {int[] t = q.poll();int a = t[0];int b = t[1];for (int i = 0; i < 4; i++) {int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a][b] + 1;q.add(new int[]{x, y});}}}return dist;}
二、飞地的数量
- 题目链接:飞地的数量
- 题目描述:
给你⼀个大小为 m x n 的⼆进制矩阵 grid ,其中 0 表示⼀个海洋单元格、 1 表示⼀个陆地 单元格。 ⼀次移动是指从⼀个陆地单元格走到另⼀个相邻(上、下、左、右)的陆地单元格或跨过 grid的边界。 返回网格中无法在任意次数的移动中离开⽹格边界的陆地单元格的数量。
示例一:
输⼊:grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释:有三个 1 被 0 包围。⼀个 1 没有被包围,因为它在边界上。
示例 2:
输⼊:grid = [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
输出:0
解释:所有 1 都在边界上或可以到达边界。
提⽰:
◦ m == grid.length
◦ n == grid[i].length
◦ 1 <= m, n <= 500
◦ grid[i][j] 的值为 0 或 1
- 解法:
算法思路: 正难则反: 从边上的 1 开始搜索,把与边上 1 相连的联通区域全部标记⼀下; 然后再遍历⼀遍矩阵,看看哪些位置的 1 没有被标记即可 标记的时候,可以用「多源 bfs 」解决。 - 代码示例:
int[] dx = {0, 0, 1, -1};int[] dy = {1, -1, 0, 0};public int numEnclaves(int[][] grid) {int m = grid.length, n = grid[0].length;Queue<int[]> q = new LinkedList<>();boolean[][] vis = new boolean[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {if (grid[i][j] == 1) {q.add(new int[]{i, j});vis[i][j] = true;}}}}while (!q.isEmpty()) {int[] t = q.poll();int a = t[0], b = t[1];for (int i = 0; i < 4; i++) {int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y]) {vis[x][y] = true;q.add(new int[]{x, y});}}}int ret = 0;for (int i = 0; i < m; i++)for (int j = 0; j < n; j++)if (grid[i][j] == 1 && !vis[i][j])ret++;return ret;}
三、地图中的最高点
- 题目链接:地图中的最高点
- 题目描述:
给你⼀个大小为 m x n 的整数矩阵 isWater ,它代表了⼀个由陆地 和水域 单元格组成的地图。
• 如果 isWater[i][j] == 0 ,格子(i, j) 是⼀个陆地格⼦。
• 如果 isWater[i][j] == 1 ,格子(i, j) 是⼀个水域格⼦。 你需要按照如下规则给每个单元格安排高度:
• 每个格⼦的高度都必须是非负的。
• 如果⼀个格子是水域 ,那么它的高度必须为 0 。
• 任意相邻的格高度差至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格⼦。(也就是说它们有⼀条公共边) 找到⼀种安排高度的方案,使得矩阵中的最高高度值最大 。 请你返回⼀个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格⼦ (i, j) 的高度。如果有多种解法,请返回任意⼀个 。
示例 1:
输入:isWater = [[0,1],[0,0]]
输出:[[1,0],[2,1]]
解释:上图展示了给各个格⼦安排的高度。 蓝⾊格子是水域格,绿色格子是陆地格。
示例二:
输⼊:isWater = [[0,0,1],[1,0,0],[0,0,0]]
输出:[[1,1,0],[0,1,1],[1,2,2]]
解释:所有安排⽅案中,最⾼可行高度为 2 。 任意安排方案中,只要最高高度为 2 且符合上述规则的,都为可行方案。
提示:
◦ m == isWater.length
◦ n == isWater[i].length
◦ 1 <= m, n <= 1000
◦ isWater[i][j] 要么是 0 ,要么是 1 。 ◦
⾄少有 1 个水域格子。
- 解法: 算法思路:
01矩阵的变型题,直接用多源 bfs 解决即可。 - 代码示例:
int[] dx = {0, 0, 1, -1};int[] dy = {1, -1, 0, 0}; public int[][] highestPeak(int[][] isWater) {int m = isWater.length, n = isWater[0].length;int[][] dist = new int[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {dist[i][j] = -1;}}Queue<int[]> q = new LinkedList<>();//所有的源点加入到序列里for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (isWater[i][j] == 1) {q.add(new int[]{i, j});dist[i][j] = 0;}}}while (!q.isEmpty()) {int[] t = q.poll();int a = t[0], b = t[1];for (int i = 0; i < 4; i++) {int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a][b] + 1;q.add(new int[]{x, y});}}}return dist;}
四、地图分析
- 题目链接:地图分析
- 题目描述:
你现在⼿⾥有⼀份⼤⼩为 n x n 的网格 grid ,上⾯的每个 单元格 都⽤ 0 和 1 标记好了。 其中 0 代表海洋, 1 代表陆地。 请你找出⼀个海洋单元格,这个海洋单元格到离它最近的陆地单元格的距离是最大的,并返回该距离。如果网格上只有陆地或者海洋,请返回 -1 。
我们这里说的距离是「曼哈顿距离」( Manhattan Distance): (x0, y0) 和 (x1, y1) 这两 个单元格之间的距离是 |x0 - x1| + |y0 - y1|
示例 1:
输入:grid = [[1,0,1],[0,0,0],[1,0,1]]
输出:2
解释:
海洋单元格 (1, 1) 和所有陆地单元格之间的距离都达到最大,最大距离为 2
⽰例 2:
输⼊:grid = [[1,0,0],[0,0,0],[0,0,0]]
输出:4
解释:
海洋单元格 (2, 2) 和所有陆地单元格之间的距离都达到最⼤,最大距离为 4。
提示:
◦ n == grid.length
◦ n == grid[i].length
◦ 1 <= n <= 100
◦ grid[i][j] 不是 0 就是 1
-
解法:
算法思路:
01矩阵的变型题,直接用多源 bfs 解决即可。 -
代码示例:
int[] dx = {0, 0, 1, -1};int[] dy = {1, -1, 0, 0}; public int maxDistance(int[][] grid) {int ret = -1;int m = grid.length, n = grid[0].length;int[][] dist = new int[m][n];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {dist[i][j] = -1;}}Queue<int[]> q = new LinkedList<>();for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 1) {dist[i][j] = 0;q.add(new int[]{i, j});}}}while (!q.isEmpty()) {int[] t = q.poll();int a = t[0], b = t[1];for (int i = 0; i < 4; i++) {int x = a + dx[i], y = b + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a][b] + 1;ret = Math.max(ret, dist[x][y]);q.add(new int[]{x, y});}}}return ret;}
结语
本文到这里就结束了,主要介绍了解决单源多源BFS问题,并通过例题深化了代码步骤,本篇文章题目十分考验代码能力,大家一定要自己尝试哦!
以上就是本文全部内容,感谢各位能够看到最后,创作不易,希望大家多多支持!
最后,大家再见!祝好!我们下期见!
相关文章:
【BFS】《单源、多源 BFS:图搜索算法的双生力量》
文章目录 前言单源BFS例题一、迷宫中离入口最近的出口二、 最小基因变化三、单词接龙四、为高尔夫比赛砍树 多源BFS例题一、 01 矩阵二、飞地的数量三、地图中的最高点四、地图分析 结语 前言 什么是单源、多源BFS算法问题呢? BFS(Breadth - First Sear…...
批量取消 PDF 文档中的所有超链接
在 PDF 文档中我们可以插入各种各样的文本也可以给文本设置字体,颜色等多种样式,同时还可以给文字或者图片添加上超链接,当我们点击超链接之后,就会跳转到对应的网页。有时候这会对我们的阅读或者使用形成一定的干扰,今…...
13.2 kubelet containerRuntime接口定义和初始化
本节重点总结 : containerRuntime 需要实现3类接口 管理容器的接口管理镜像的接口Streaming API 用于客户端与容器进行交互 type KubeGenericRuntime interface {kubecontainer.Runtimekubecontainer.StreamingRuntimekubecontainer.CommandRunner }containerRun…...
使用 gone.WrapFunctionProvider 快速接入第三方服务
项目地址:https://github.com/gone-io/gone 本文中源代码: esexamples/es 文章目录 1. gone.WrapFunctionProvider 简介2. 配置注入实现3. 实战示例:Elasticsearch 集成4. 使用方式5. 最佳实践6. 总结 在如何给Gone框架编写Goner组件…...
git 标签学习笔记
目录 轻量级标签 带注释的标签(推荐) 给指定 commit 打标签 推送单个标签,需要单独推送,代码推送不会推送标签 推送所有标签 删除标签 轻量级标签 git tag v1.0.0 只是简单地给当前 commit 打上 v1.0.0 标签。 带注释的标…...
【论文阅读】基于思维链提示的大语言模型软件漏洞发现与修复方法研究
这篇文章来自于 Chain-of-Thought Prompting of Large Language Models for Discovering and Fixing Software Vulnerabilities 摘要 软件安全漏洞在现代系统中呈现泛在化趋势,其引发的社会影响日益显著。尽管已有多种防御技术被提出,基于深度学习&…...
企业在人工智能创新与安全之间走钢丝
2025 年全球 AI/ML 工具使用量将激增,企业将 AI 融入运营之中,员工也将 AI 嵌入日常工作流程中。报告显示,企业对 AI/ML 工具的使用同比增长 3,000% 以上,凸显了各行各业迅速采用 AI 技术,以提升生产力、效率和创新水平…...
CSS动画
目录 一、核心概念与语法 1. keyframes 关键帧 2. animation 属性 二、动画调速函数(animation-timing-function) 1. 预设值 2. 贝塞尔曲线 3. 步进函数(steps()) 三、动画控制与交互 1. 暂停与恢复 2. JavaScript 控制…...
计算机视觉(CV)技术的优势和挑战
计算机视觉(CV)技术是人工智能领域中的一个重要分支,它主要通过让机器学会“看”和“理解”图像或视频来模拟人类视觉系统。以下是计算机视觉技术的一些优势和挑战: 优势: 自动化:计算机视觉技术可以实现…...
动态IP与静态IP该如何选?
一、当IP地址成为"网络身份" 2023年亚马逊封号潮中,某杭州卖家因登录IP频繁切换(早8点在纽约,午间瞬移到东京),触发平台风控导致账号冻结。这类"时空错乱症"揭示了跨境电商的生存法则:…...
Vue.js 完全指南:从入门到精通
1. Vue.js 简介 1.1 什么是 Vue.js? Vue.js(通常简称为 Vue)是一个用于构建用户界面的渐进式 JavaScript 框架。所谓"渐进式",意味着 Vue 的设计是由浅入深的,你可以根据自己的需求选择使用它的一部分或全部功能。 Vue 最初由尤雨溪(Evan You)在 2014 年创…...
《TypeScript 7天速成系列》第3天:TypeScript高级类型通关秘籍:泛型+联合+交叉类型实战
TypeScript 的类型系统是其最强大的特性之一,但也是许多开发者感到困惑的地方。今天我们就来破解 TypeScript 中最难的类型系统,掌握泛型、联合类型和交叉类型的使用技巧。 一、泛型函数与泛型接口 泛型是 TypeScript 中创建可重用组件的重要工具&…...
Python----数据分析(足球运动员数据分析)
一、数据展示 1.1、数据 1.2、列名 字段名备注Name姓名Nationality国籍National_Position国家队位置National_Kit国家队号码Club所在俱乐部Club_Position所在俱乐部位置Club_Kit俱乐部号码Club_Joining加入俱乐部时间Contract_Expiry合同到期时间Rating评分Height身高Weight体…...
音视频 三 看书的笔记 MediaPlayer的C/S架构
MediaPlayer在运行时分为Client和Server两部分 Client层:位于Java层,用户通过调用Java层的API(如setDataSource)来操作MediaPlayer。 Server层:位于C层,负责实际的媒体处理工作。Server层通过Binder机…...
Elasticsearch:使用 AI SDK 和 Elastic 构建 AI 代理
作者:来自 Elastic Carly Richmond 你是否经常听到 AI 代理(AI agents)这个词,但不太确定它们是什么,或者如何在 TypeScript(或 JavaScript)中构建一个?跟我一起深入了解 AI 代理的概…...
echarts添加坐标轴点击事件
echarts添加坐标轴点击事件 chart.on(click, (params) > {if(params.componentType yAxis && this.type ! 1){console.log(params);// 检查是否点击了系列数据console.log(你点击了 ${params.name} 的数据点,值为 ${params.value}); this.$bus.$emi…...
如何在linux中部署dns服务 主备dns (详细全过程)
环境centos 7.9 主DNS:192.168.60.131 备DNS:192.168.60.134 我以 chenxingyu0.com 指向 192.168.60.200为例 首先是主dns #!/bin/bash# 检查是否为 root 用户 if [ "$(id -u)" ! "0" ]; thenecho "请使用…...
GitLab 中文版17.10正式发布,27项重点功能解读【二】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
matplotlib——南丁格尔玫瑰
南丁格尔玫瑰图(Nightingale Rose Chart),是一种特殊形式的柱状图,它以南丁格尔(Florence Nightingale)命名,她在1858年首次使用这种图表来展示战争期间士兵死亡原因的数据。 它将数据绘制在极坐…...
WPF 与 C# 融合开发:从基础到高级应用(一)
WPF 与 C# 融合开发:从基础到高级应用 一、C# 语言基础回顾 1.1 C# 语言概述 C# 是微软开发的一种现代、面向对象的编程语言,它融合了 C、C 和 Java 等语言的优点,具有简洁、安全、高效等特点。C# 广泛应用于 Windows 平台的应用开发&…...
ref和reactive区别
在 Vue 3 中,ref 和 reactive 是两种创建响应式数据的主要 API,但它们的适用场景和使用方式有所不同。以下是它们的核心区别和示例: 一、核心区别 特性refreactive适用数据类型所有类型(基本类型、对象、数组)仅对象或…...
精选10个好用的WordPress免费主题
10个好用的WordPress免费主题 1. Astra Astra 是全球最受欢迎的 WordPress 主题。它功能丰富,易于使用,SEO友好,是第一个安装量突破100万的非默认主题,并获得了5000多个五星好评。 它完美集成了Elementor、Beaver,古…...
DerpNStink: 1靶场渗透
DerpNStink: 1 来自 <DerpNStink: 1 ~ VulnHub> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182,靶场IP192.168.23.213 3,对靶机进行端…...
apache安装脚本使用shell建立
注意防火墙,yum,网络连接等 以下是具体的apache安装脚本 #!/bin/bash # Set Apache version to install ## author: yuan # 检查外网连接 echo "检查外网连接..." ping www.baidu.com -c 3 > /dev/null 2>&1 if [ $? -eq 0 ]; …...
Azure SDK 使用指南
Azure SDK(软件开发工具包)是一组由微软提供的工具和库,旨在帮助开发者以多种编程语言(如 .NET、Java、Python、JavaScript 等)与 Azure 服务进行交互。 通过使用 Azure SDK,开发者可以更高效地构建、部…...
DeepSeek-V3-0324 版本升级概要
DeepSeek-V3-0324 魔搭社区汇聚各领域最先进的机器学习模型,提供模型探索体验、推理、训练、部署和应用的一站式服务。https://modelscope.cn/models/deepseek-ai/DeepSeek-V3-0324 发布背景与改进 根DeepSeek-V3-0324 展示了以下关键改进: 推理性能提…...
leetcode 150. 逆波兰表达式求值
150. 逆波兰表达式求值 - 力扣(LeetCode) class Solution:def evalRPN(self, tokens: List[str]) -> int:stack[]for item in tokens:if item not in ( ,-,* , / ):stack.append(item)else:preint(stack.pop())pre_beforeint(stack.pop())sign itemi…...
LangChain4j与DashScope深度集成实战:一站式开发指南
本篇文章会通篇详细的讲清楚LangChain4j与DashScope集成的各个方面,从Springboot的集成到Ai对话、会话记忆、RAG、FunctionCalling、互联网搜索、结构化的输出、多模态等都给出相应的说明,希望通过这篇文章对于LLM不了解的同仁一样可以扩展出自己的AI应用…...
逼用户升级Win11,微软开始给Win10限速
随着Windows10的支持时间越来越短,微软也加大了对Win10用户的驱赶力度。 最近,微软官宣了将要在今年6月份降低OneNote for Windows 10的同步速度。软件也将和Windows10在今年的10月14日一同停止支持和维护。 这将影响实时协作和多设备访问。 对OneNote…...
工作流引擎Flowable介绍及SpringBoot整合使用实例
Flowable简介 Flowable 是一个轻量级的业务流程管理(BPM)和工作流引擎,基于 Activiti 项目发展而来,专注于提供高性能、可扩展的工作流解决方案。它主要用于企业级应用中的流程自动化、任务管理和审批流等场景。 Flowable 的核心…...
推荐一个可以自定义github主页的网站
一、简介 Profile Readme Generator 是一个开源工具,可以帮助你快速创建个性化的 GitHub 个人简介(README)。它支持自定义内容和样式,让你的 GitHub 个人主页更加美观和专业。 二、使用步骤 (一)访问网站…...
【R语言可视化】相关系数热图
目录 热图无显著性 结果展示01: 热图显著性 结果展示02: ggplot2绘制三角热图 结果展示03: corrplot绘制三角热图 结果展示04: 热图无显著性 # 示例数据 data(mtcars) df <- mtcars# 计算相关矩阵 cor_matrix <- round(cor(df…...
【区块链 + 文化版权】文创链 | FISCO BCOS 应用案例
“文创链”是由四川省区块链行业协会、成都音像出版社有限公司共同发起, 由成都九天星空科技有限公司等联合打造的数字文创领域联盟链。平台采用FISCO BCOS 开源底层框架, 为数字文创产业构建一个高效、透明、可信的版权管理与交易平台。 平台专注于数字…...
# 使用自定义Shell脚本hello快速配置Linux用户账户
使用自定义Shell脚本快速配置Linux用户账户 在学校实验室管理Linux服务器,或者公司小团队管理服务器时,大家需要一个能隔离自己服务,但是自己又需要对服务器的完整权限的情形。创建和配置用户账户是一项常见但繁琐的任务。特别是当你需要频繁…...
PyTorch中的Tensor
PyTorch中的Tensor 是核心数据结构,类似于 NumPy 的多维数组,但具备 GPU 加速和自动求导等深度学习特性。 一、基本概念 核心数据结构 Tensor 是存储和操作数据的基础单元,支持标量(0D)、向量(1D&am…...
16-CSS3新增选择器
知识目标 掌握属性选择器的使用掌握关系选择器的使用掌握结构化伪类选择器的使用掌握伪元素选择器的使用 如何减少文档内class属性和id属性的定义,使文档变得更加简洁? 可以通过属性选择器、关系选择器、结构化伪类选择器、伪元素选择器。 1. 属性选择…...
关于笔记本电脑突然没有wifi图标解决方案
笔记本电脑突然没有wifi图标解决方案,设置里也看不见wifi,电脑突然就连不网络了 解决方案: 我的电脑——>管理——>服务和应用程序——>服务——>找到WLAN AutoConfig——>点击启动就好了...
Pytorch学习笔记(七)Learn the Basics - Optimizing Model Parameters
这篇博客瞄准的是 pytorch 官方教程中 Learn the Basics 章节的 Optimizing Model Parameters 部分。 官网链接:https://pytorch.org/tutorials/beginner/basics/optimization_tutorial.html 完整网盘链接: https://pan.baidu.com/s/1L9PVZ-KRDGVER-AJnXOvlQ?pwd…...
数据可视化TensorboardX和tensorBoard安装及使用
tensorBoard 和TensorboardX 安装及使用指南 tensorBoard 和 TensorBoardX 是用于可视化机器学习实验和模型训练过程的工具。TensorBoard 是 TensorFlow 官方提供的可视化工具,而 TensorBoardX 是其社区驱动的替代品,支持 PyTorch 等其他框架。以下是它…...
工业4G路由器赋能智慧停车场高效管理
工业4G路由器作为智慧停车场管理系统通信核心,将停车场内的各个子系统连接起来,包括车牌识别系统、道闸控制系统、车位检测系统、收费系统以及监控系统等。通过4G网络,将这些系统采集到的数据传输到云端服务器或管理中心,实现信息…...
深度学习1—Python基础
深度学习1—python基础 你的第一个程序 print(hello world and hello deep learning!)基本数据结构 空值 (None):在 Python 中,None 是一个特殊的对象,用于表示空值或缺失的值。它不同于数字 0,因为 0 是一个有意义的数字&#…...
数据结构十三、set map
一、set 1、size / empty size:返回set中实际元素的个数 empty:判断set是否为空 2、begin / end 这是两个迭代器,因此可以使用范围for来遍历整个红黑树。其中,遍历是按照中序遍历的顺序,因此是一个有序序列。 3、in…...
【大模型基础_毛玉仁】3.5 Prompt相关应用
目录 3.5 相关应用3.5.1 基于大语言模型的Agent3.5.2 数据合成3.5.3 Text-to-SQL3.5.4 GPTs 3.5 相关应用 Prompt工程应用广泛,能提升大语言模型处理基础及复杂任务的能力,在构建Agent、数据合成、Text-to-SQL转换和设计个性化GPTs等方面不可或缺。 . …...
自动驾驶VLA模型技术解析与模型设计
1.前言 2025年被称为“VLA上车元年”,以视觉语言动作模型(Vision-Language-Action Model, VLA)为核心的技术范式正在重塑智能驾驶行业。VLA不仅融合了视觉语言模型(VLM)的感知能力和端到端模型的决策能力,…...
【AI】Orin NX+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功
【AI】郭老二博文之:AI学习目录汇总 1、烧写系统 新到的开发板,已经烧写好Ubuntu系统,版本为22.04。 如果没有升级到Ubuntu22.04,可以在电脑Ubuntu系统中使用SDKManager来烧写Ubuntu系统,网络情况好的话,也可以直接将CUDA、cuDNN、TensorRT、Deepstream等也安装上。 2…...
vscode 通过Remote-ssh远程连接服务器报错 could not establish connection to ubuntu
vscode 通过Remote-ssh插件远程连接服务器报错 could not establish connection to ubuntu,并且出现下面的错误打印: [21:00:57.307] Log Level: 2 [21:00:57.350] SSH Resolver called for "ssh-remoteubuntu", attempt 1 [21:00:57.359] r…...
ESP32S3 WIFI 实现TCP服务器和静态IP
一、 TCP服务器代码 代码由station_example_main的官方例程修改 /* WiFi station ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an &q…...
第三课:Stable Diffusion图生图入门及应用
文章目录 Part01 图生图原理Part02 图生图基本流程Part03 随机种子作用解析Part04 图生图的拓展应用 Part01 图生图原理 当提示词不能足够表达用户需求的时候,加入图片能让AI更好的理解你的想法图片上的像素信息会在加噪和去噪的过程中,作为一种特征反映…...
蓝桥与力扣刷题(蓝桥 蓝桥骑士)
题目:小明是蓝桥王国的骑士,他喜欢不断突破自我。 这天蓝桥国王给他安排了 N 个对手,他们的战力值分别为 a1,a2,...,an,且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战,也可以选择避战。 身为高傲的骑士&a…...
Photoshop怎样保存为ico格式
1. 打开图像 开启 Photoshop 软件,选择 “文件” 菜单,点击 “打开” 选项,然后找到你想要保存为 ICO 格式的图像文件并打开。 2. 调整图像大小(可选) ICO 图标通常有特定尺寸要求,你可以根据需求调整图像…...