- 第 171 篇 -
Date: 2026 - 02- 24 | 周二
Author: 郑龙浩(仟墨)
算法:图论 - BFS DFS 并查集
复习之前做过的部分
总结一下DFS和BFS以及合并查集的题目
文章目录
- [DFS BFS](#DFS BFS)
- 并查集
-
- [1- 并查集 - 基础写法](#1- 并查集 - 基础写法)
- [2 - 并查集 - 压缩版](#2 - 并查集 - 压缩版)
- 3-卡码网107-寻找存在的路线-并查集-第2次刷题
- 4-卡码网108-冗余连接
DFS BFS
1_卡码网98_可达路径2_DFS
cpp
// 1_卡码网98_可达路径2_DFS
// Date: 2026-02-19 Author: 郑龙浩 算法: BFS
#include "bits/stdc++.h"
using namespace std;
vector <vector <int>> result; // 存储多个path
vector <int> path; // 存放一条路径
void dfs(vector <vector <int>>& graph, int x, int n) {
// 1 找到n后就将本条路径存入result
if (x == n) {
path.push_back(x); // 记得将最后一个节点插入
result.push_back(path);
return;
}
path.push_back(x); // 只要传送进来的x是合法的,所以一定要存入路径
// 2 遍历节点x连接的所有节点 / 或叫做遍历graph的每一行
for (int i = 1; i <= n; i++) {
if (graph[x][i] == 1) { // 如果找到了下一个连接节点,就以此为基础,使用dfs找到周围连接的节点
dfs(graph, i, n);
path.pop_back(); // 回溯
}
}
}
int main(void) {
ios::sync_with_stdio;
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> graph(N + 1, vector <int> (N + 1));
int s, t;
// 输入graph邻接矩阵
while (M--) {
cin >> s >> t;
graph[s][t] = 1; // 将路径关系存储到邻接矩阵graph
}
// 找到所有路径
dfs(graph, 1, N);
for (auto path : result) {
int len = path.size();
for (int i = 0; i < len - 1; i++) cout << path[i] << ' ';
cout << path[len - 1] << '\n';
}
return 0;
}
cpp
3_卡码99_计数孤岛_BFS
cpp
// Date: 2026-02-20 Author:郑龙浩
// 3_卡码99_计数孤岛_BFS
// 算法:BFS DFS
// 第二次刷题
// 用时:
#include "bits/stdc++.h"
using namespace std;
int cnt = 0; // 岛屿的数量
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
// 深搜dfs搜索周围的岛屿
void dfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0 || visited[x][y] == true) { // 如果越界 + 是海洋 + 访问过,就return
return;
}
visited[x][y] = true; // 只要是没越界, 是陆地,没访问,就记录成访问
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
dfs(grid, visited, nextX, nextY);
}
}
}
queue <pair <int, int>> que;
void bfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
int height = grid.size();
int width = grid[0].size();
que.push({x, y}); // 先将传来的push里面
visited[x][y] = true;
while (!que.empty()) {
pair <int, int> cur = que.front();
que.pop();
int nextX, nextY;
for (int i = 0; i < 4; i++) {
nextX = cur.first + direction[i][0];
nextY = cur.second + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
que.push({nextX, nextY});
visited[nextX][nextY] = true; // 当前位置标记一下
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M, 0)); // 岛屿的分布的矩阵
vector <vector <bool>> visited(N, vector <bool> (M, 0)); // 访问矩阵
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1 && visited[i][j] == false) { // 如果陆地 + 没访问过,就dfs搜索周围
bfs(grid, visited, i, j);
cnt++;
}
}
}
cout << cnt;
return 0;
}
4-卡码网100-最大岛屿的面积
cpp
// Date: 2026-02-20 - 21 Author:郑龙浩
// 4-卡码网100-最大岛屿的面积
// 算法:BFS DFS
// 第二次刷题
// 用时:
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int Max = 0, S = 0;
// 深搜dfs搜索周围的岛屿
void dfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0 || visited[x][y] == true)
return;
S++;
visited[x][y] = true;
int nextX, nextY; // 下1个坐标
for (int i = 0; i < 4; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
bool IS = nextX >= 0 && nextX < height && nextY >= 0 && nextY < width;
if (IS && grid[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
dfs(grid, visited, nextX, nextY);
}
}
}
// BFS
void bfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
queue <pair <int, int>> que;
int height = grid.size();
int width = grid[0].size();
visited[x][y] = true;
que.push({x, y});
S++;
pair <int, int> cur;
int nextX, nextY;
while (!que.empty()) {
cur = que.front();
que.pop();
for (int i = 0; i < 4; i++) {
nextX = cur.first + direction[i][0];
nextY = cur.second + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1 && visited[nextX][nextY] == false) {
que.push({nextX, nextY});
visited[nextX][nextY] = true;
S++;
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M, 0));
vector <vector <bool>> visited(N, vector <bool> (M, 0));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1 && visited[i][j] == false) {
S = 0;
bfs(grid, visited, i, j); // 搜索周围的陆地,并计算出面积
Max = max(S, Max);
}
}
}
cout << Max;
return 0;
}
5_卡码网101_孤岛的总面积
cpp
// Date: 2026-02-09 Author:郑龙浩
// 5_卡码网101_孤岛的总面积
// 题目要求计算"孤岛"的总面积
// 方法1:DFS 方法2: BFS
// 思路:
// 孤岛定义:位于矩阵内部且不与边缘接触的陆地组成的岛屿
// 将所有与边缘接触的岛屿变成海洋,然后计算所有的陆地
// 用时:
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int S = 0;
// DFS作用:深搜与边缘接触的陆地陆地,且将搜索到的陆地变为海洋
// 法1
void dfs(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0)
return;
grid[x][y] = 0; // 没越界也是陆地,就变成海洋
for (int i = 0; i < 4; i++) {
int nextX = x + direction[i][0];
int nextY = y + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
dfs(grid, nextX, nextY);
}
}
}
// 法2
void bfs(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
queue <pair <int, int>> que;
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0)
return;
que.push({x, y});
grid[x][y] = 0; // 将传来的陆地变成海洋
while (!que.empty()) {
pair <int, int> cur = que.front();
que.pop();
for (int i = 0; i < 4; i++) {
int nextX = cur.first + direction[i][0];
int nextY = cur.second + direction[i][1];
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
que.push({nextX, nextY});
grid[nextX][nextY] = 0;
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
// 将边缘的岛屿变成海洋
for (int i = 0; i < N; i++) {
if (grid[i][0] == 1)
bfs(grid, i, 0);
if (grid[i][M - 1] == 1)
bfs(grid, i, M - 1);
}
for (int j = 0; j < M; j++) {
if (grid[0][j] == 1)
bfs(grid, 0, j);
if (grid[N - 1][j] == 1)
bfs(grid, N - 1, j);
}
for (int i = 1; i < N - 1; i++) {
for (int j = 1; j < M - 1; j++) {
if (grid[i][j] == 1) S++;
}
}
cout << S;
return 0;
}
6_卡码网102_沉没孤岛_dfsDFS
cpp
// Date: 2026-02-22 Author:郑龙浩
// 6_卡码网102_沉没孤岛_dfsDFS
// 方法1:DFS 方法2: dfs
// 思路:
// 孤岛定义:位于矩阵内部且不与边缘接触的陆地组成的岛屿
// 和 5_卡码网101_孤岛的总面积 的做法类似,只不过这次边缘岛屿标记为了2
// 1 表示内陆,2 表示边缘陆地,0 表示海洋
// 用时:
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int S = 0;
// DFS作用:深搜陆地,且将搜索到的陆地变为海洋
// 法1
void dfs(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] == 0 || grid[x][y] == 2) {
return;
}
grid[x][y] = 2; // 边缘陆地标记为2
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + x;
int nextY = direction[i][1] + y;
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1)
dfs(grid, nextX, nextY);
}
}
// 法2
void bfs(vector <vector <int>>& grid, int x, int y) {
int height = grid.size();
int width = grid[0].size();
queue <pair <int, int>> que;
que.push({x, y});
while (!que.empty()) {
pair <int, int> cur = que.front();
que.pop();
// 搜寻四周
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + cur.first;
int nextY = direction[i][1] + cur.second;
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && grid[nextX][nextY] == 1) {
que.push({nextX, nextY});
grid[nextX][nextY] = 2;// 加入que就相当于访问
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M;
cin >> N >> M;
vector <vector <int>> grid(N, vector <int>(M, 0));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
// 将边缘的岛屿变成2
for (int i = 0; i < N; i++) {
if (grid[i][0] == 1)
bfs(grid, i, 0);
if (grid[i][M - 1] == 1)
bfs(grid, i, M - 1);
}
for (int j = 0; j < M; j++) {
if (grid[0][j] == 1)
bfs(grid, 0, j);
if (grid[N - 1][j] == 1)
bfs(grid, N - 1, j);
}
for (int i = 1; i < N - 1; i++) {
for (int j = 1; j < M - 1; j++) {
if (grid[i][j] == 1) grid[i][j] = 0;
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 2) cout << 1;
else cout << grid[i][j];
cout << ' ';
}
cout << '\n';
}
return 0;
}
7_卡码网103_高山流水_BFS_DFS
cpp
// Date: 2026-02-22 Author:郑龙浩
// 7_卡码网103_高山流水_BFS_DFS
// 逆向思维思考:
// 也就是本身水是从中间向两边界流,现在是从两个边界往中间流,即从低往高流
// - 然后所有第一边界搜索的的坐标用first_visited数组去标记
// - 第二边界的用second_visited数组去标记
// 两者共同搜索过的坐标就是既可以到达第一也可以到到第二边界的坐标
// 方法1:DFS 方法2: BFS
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
void dfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (visited[x][y] == true) return; // 只要访问过,就return
visited[x][y] = true;
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + x;
int nextY = direction[i][1] + y;
// 下一个坐标没越界 且 下一个坐标没访问过 且 下一个坐标的高度大于当前坐标的高度,才会以下一个坐标为基础向四周搜索
if (nextX >= 0 && nextX < height && nextY >= 0 && nextY < width && visited[nextX][nextY] == false && grid[nextX][nextY] >= grid[x][y]) {
dfs(grid, visited, nextX, nextY);
}
}
}
void bfs(vector <vector <int>>& grid, vector <vector <bool>>& visited, int x, int y) {
int height = grid.size();
int width = grid[0].size();
if (visited[x][y] == true) return; // 只要访问过,就return
visited[x][y] = true;
queue <pair <int, int>> que;
que.push({x, y});
while (!que.empty()) {
pair <int, int> cur = que.front();
que.pop(); // 取出当前位置,就要将位置从que中去掉
// 查找周围的陆地
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + cur.first;
int nextY = direction[i][1] + cur.second;
bool IS = nextX >= 0 && nextX < height && nextY >= 0 && nextY < width; // 越界测试
if (IS && visited[nextX][nextY] == false && grid[nextX][nextY] >= grid[cur.first][cur.second]) { // 没有越界 && 没访问过 && 下一个位置高于当前位置,就继续访问下一个位置周围坐标
que.push({nextX, nextY});
visited[nextX][nextY] = true; // 只要加入que,就是访问的
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M; cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M, 0));
vector <vector <bool>> first_visited(N, vector <bool> (M, false)); // 可以到第一边界的水
vector <vector <bool>> second_visited(N, vector <bool> (M, false)); // 可以到第二边界的水
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
// 第一边界:上 + 第二边界:下
for (int i = 0; i < M; i++) {
bfs(grid, first_visited, 0, i);
bfs(grid, second_visited, N - 1, i);
}
// 第一边界:左 + 第二边界:右
for (int i = 0; i < N; i++) {
bfs(grid, first_visited, i, 0);
bfs(grid, second_visited, i, M - 1);
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (first_visited[i][j] == true && second_visited[i][j] == true)
cout << i << ' ' << j << '\n';
}
}
return 0;
}
8_卡码网104_建造最大岛屿
cpp
// Date: 2026-02-22 周日 Author:郑龙浩
// 8_卡码网104_建造最大岛屿
// 用时:2 h 11 min
// 大概描述的话就是:
// - 使用dfs/bfs搜索岛屿,且对每个岛屿做不同的编号mark的标记,这个编号是直接写到了grid原数组中的
// - 然后用unordered_map<int ,int> gridS;记录每个岛屿(即编号)的面积:循环便利所有的点,然后girdS[mark]++不断累加每个岛屿的面积
// - 然后遍历所有点,如果遇到了海洋,就然后查找「上下左右」四个方向的陆地,如果是陆地就取出其陆地编号,并利用gridS去查找对应陆地的面积,然后将面积加入到S中,以此类推,去判断四个方向,然后如果是陆地就加上其面积
// - 注意:添加过面积的岛屿不要重复添加
// - 可能会存在比如,该海洋 左边 上边 的陆地是同一个岛屿的情况,此时左边 和 上边 如果都加到S中的话,同一个岛屿的面积就会计算两次
// - 所以为了杜绝这种重复计算同一个岛屿的情况,在寻找四个方向的陆地的时候,需要判断四个方向的陆地是否是同一个岛屿,如果是的话,就只在S中加一次该岛屿的面积
// - 写一个unordered_set <int> visited_mark 记录每个岛屿是否被添加过, 且每次遇到新海洋的时候都要记得clear一下vidited
// 1 遍历所有陆地,用DFS/BFS给每个岛屿编号,并计算每个岛屿的面积,存入grid_mark_S。
// 2 遍历所有海洋,计算如果把它变成陆地,加上周围不同岛屿的面积,得到可能的最大岛屿面积。
// 特殊情况,全是陆地
// 方法1:DFS 方法2: BFS
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
// grid是矩阵,mark是岛屿的编号,cnt是岛屿面积,x, y 是现在的坐标
// 搜索 (x, y) 以及四周的坐标点
void dfs(vector <vector <int>>& grid, int mark, int& cnt, int x, int y) {
int height = grid.size(), width = grid[0].size();
if (x < 0 || x >= height || y < 0 || y >= width || grid[x][y] >= 2) // 只要越界 + 访问过的岛屿,就return
return;
grid[x][y] = mark; // 对访问过坐标左mark
cnt++;
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + x;
int nextY = direction[i][1] + y;
bool IS = nextX >= 0 && nextX < height && nextY >= 0 && nextY < width;
if (IS && grid[nextX][nextY] == 1) { // 没越界 && 是没访问过的陆地
dfs(grid, mark, cnt, nextX, nextY);
}
}
}
// 我采用的是 入队就是访问
// 我原来的写的出队就是访问做法,如果少写了pop出来后判断是否访问过的话,就会出现重复访问多算面积的情况,
// 有可能会在入对的时候,入队多次,因为在入队的时候有可能那个坐标已经在que中了只是还没有循环到出队那一步
// 而且que中可能会加入多个相同的坐标, 需要多加一个pop后坐标判断
void bfs(vector <vector <int>>& grid, int mark, int& cnt, int x, int y) {
int height = grid.size(), width = grid[0].size();
queue <pair <int, int>> que;
que.push({x, y});
grid[x][y] = mark;
cnt++;
while (!que.empty()) {
pair <int, int> cur = que.front();
que.pop();
for (int i = 0; i < 4; i++) {
int nextX = direction[i][0] + cur.first;
int nextY = direction[i][1] + cur.second;
bool IS = nextX >= 0 && nextX < height && nextY >= 0 && nextY < width; // 越界判断
if (IS && grid[nextX][nextY] == 1) {
que.push({nextX, nextY});
grid[nextX][nextY] = mark;
cnt++;
}
}
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, M, mark = 2; cin >> N >> M;
vector <vector <int>> grid(N, vector <int> (M));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j ++) {
cin >> grid[i][j];
}
}
unordered_map <int, int> islandS; // 岛屿编号 + 面积
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 1) { // 发现了1,就是发现了没有被标记的岛屿,也就是没有访问过的岛屿
int cnt = 0;
bfs(grid, mark, cnt, i, j);
islandS[mark++] = cnt;
}
}
}
if (islandS.empty()) { // 如果全是海洋,就只打印1,只可能是一个陆地
cout << 1;
return 0;
}
unordered_set <int> visited; // 存储遇到的岛屿,避免重复计算相同编号的岛屿
int Max = 0;
for (auto item : islandS) { // 如果全是陆地,不会执行后面的代码,就必须保证max是最大岛屿
Max = max(item.second, Max);
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (grid[i][j] == 0) { // 尝试将每个海洋坐标点作为一个陆地,组成新的岛屿
visited.clear(); // 重新遇到岛屿
int newS = 1; // 新面积,初值 1
for (int k = 0; k < 4; k++) {
int nextX = direction[k][0] + i;
int nextY = direction[k][1] + j;
bool IS = nextX >= 0 && nextX < N && nextY >= 0 && nextY < M; // 越界判断00
if (IS && grid[nextX][nextY] >= 2 && visited.find(grid[nextX][nextY]) == visited.end()) { // 不越界 && 没有遇到过的岛屿 && 是陆地, 就计入新岛屿面积中
newS += islandS[grid[nextX][nextY]]; // 遇到的岛屿面积加入到总面积newS中去
visited.insert(grid[nextX][nextY]);// 标记当前岛屿为访问过
}
}
Max = max(newS, Max); // 保留最大S
}
}
}
cout << Max;
return 0;
}
9_卡码网106_海岸线计算
cpp
// Date: 2026-02-23 Author:郑龙浩
// 9_卡码网106_海岸线计算
// 用时:
// 不需要使用DFSBFS也能做,直接遍历判断即可
// 遇到陆地时,就判断四周是否是海洋或者边界,有几个海洋或者陆地就+几
#include "bits/stdc++.h"
using namespace std;
int direction[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int cnt = 0;
int N, M; cin >> N >> M;
vector <vector <int>> grid(N, vector <int>(M, 0));
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
cin >> grid[i][j];
}
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
int nextX, nextY;
if (grid[i][j] == 1) { // 如果是陆地,就要判断周围是否是海洋,如果是海洋就算是一个海岸线
for (int k = 0; k < 4; k++) {
nextX = i + direction[k][0];
nextY = j + direction[k][1];
if (nextX < 0 || nextX >= N || nextY < 0 || nextY >= M || grid[nextX][nextY] == 0)
cnt++;
}
}
}
}
cout << cnt;
return 0;
}
10_卡码网110_字符串迁移
cpp
// Date: 2026-02-23 周四 Author:郑龙浩
// 10_卡码网110_字符串迁移
// 算法:BFS
// 用时:
#include "bits/stdc++.h"
using namespace std;
string beginStr, endStr;
unordered_set <string> strList;
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N; cin >> N;
cin >> beginStr >> endStr;
string word;
for (int i = 0; i < N; i++) {
cin >> word;
strList.insert(word);
}
unordered_map <string, int> visited; // 存放访问过的路径
int path = 1; // 到达某节点的路径的长度
queue <string> que;
que.push(beginStr); // 将初始String存入que待取出队列中去
visited[beginStr] = path; // 刚开始就忘了这个,初始的String也算搜索的String之一,也要被标记为访问过
while (!que.empty()) {
string cur_word = que.front();
que.pop();
// 找出所有可能的单词,只要是在strList中的单词,都是可以转换的单词
int len = cur_word.size();
string new_word;
// 【注意】我又忘记写了
path = visited[cur_word]; // 记得将path更新为到达word节点的最近的长度
for (int i = 0; i < len; i++) { // 确定更换哪个字符
new_word = cur_word;
for (int j = 0; j < 26; j++) { // 在某个字符处,尝试26个字母的更替
new_word[i] = j + 'a';// 组成后的新的单词
if (new_word == endStr) { // 如果终点了,此时就是最短路径(因为BFS是一圈圈跟波纹一样去搜索的)
cout << path + 1; // 打印路径length
return 0;
}
// 如果new_word在strList中就表示beginStr就可以转换为该新单词 && 没访问过,也就可以标记为访问过 + 记录到该节点的路径长度了
else if (strList.find(new_word) != strList.end() && visited.find(new_word) == visited.end()) {
que.push(new_word); // 将new_word放入待处理队列
visited[new_word] = path + 1; // 之前的路径长度加上新的路径(且新路径的长度为1),也就是之前的路径+1
}
}
}
}
cout << 0; // 只有beginStr与endStr不能连接的时候,前面的代码中才不会执行 cout << path 而且return,也就是没有任何路径可以到达终点的时候才会打印0
return 0;
}
11_卡码网105_有向图的完全连通
cpp
// Date: 2026-02-23 Author:郑龙浩
// 11_卡码网105_有向图的完全连通
// 算法:BFS
// 思路:
// 使用BFS或者DFS,从节点1搜索到节点N,只要搜索过,就用visied标记一下。\
最后遍历所有节点,如果visited中有false, 就是存在没有访问的节点,就输出-1,如果全都是true,就输出1
// 因为本题可能会出现节点多边少的情况,所以使用邻接表
// 如果使用矩阵,可能会存在浪费空间的情况
#include "bits/stdc++.h"
using namespace std;
// 从1开始搜索,直到最后
void dfs(vector <list <int>>& graph, vector <bool>& visited, int cur) {
if (visited[cur] == true) { // 只要访问过cur,就return,无需访问
return;
}
visited[cur] = true;
for (auto item : graph[cur]) { // 取出所有与cur相连的节点
if (visited[item] == false) // 只要没访问过item,就用dfs深搜周围
dfs(graph, visited, item);
}
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int N, K;
cin >> N >> K;
vector <list <int>> graph(N + 1);
vector <bool> visited(N, false);
int s, t;
while (K--) {
cin >> s >> t;
graph[s].push_back(t);
}
dfs(graph, visited, 1);
int ans = 1;
for (int i = 1; i <= N; i++) {
if (visited[i] == false) {
ans = -1;
}
}
cout << ans;
return 0;
}
并查集
1- 并查集 - 基础写法
cpp
// Date: 2026-02-23
// Author: 郑龙浩
#include "bits/stdc++.h"
using namespace std;
const int N = 1000; // 节点数量
int father[N]; // 存储所有节点的父节点的数组
void init(int n) { // 初始化
for (int i = 0; i < n; i++) {
father[i] = i; // 让所有i节点的父节点是自己i
}
}
// 查找根节点 - 普通版
int find_root(int x) {
if (father[x] == x) {
return x; // 如果x自己就是根节点,就直接返回x
} else { // 如果x自己不是根节点就递归找到根节点,然后返回
return find_root(father[x]);
}
}
// 合并集合
void join(int x, int y) {
int rootX = find_root(x);
int rootY = find_root(y);
if (rootX == rootY) return;
father[rootY] = rootX;
}
bool isSame(int x, int y) {
return find_root(x) == find_root(y);
}
int main(void) {
return 0;
}
2 - 并查集 - 压缩版
cpp
// Date: 2026-02-23
// Author: 郑龙浩
#include "bits/stdc++.h"
using namespace std;
const int N = 1000; // 节点数量
int father[N]; // 存储所有节点的父节点的数组
void init(int n) { // 初始化
for (int i = 0; i < n; i++) {
father[i] = i; // 让所有i节点的父节点是自己i
}
}
/*
// 查找根节点 - 普通版
int find_root(int x) {
if (father[x] == x) {
return x; // 如果x自己就是根节点,就直接返回x
} else { // 如果x自己不是根节点就递归找到根节点,然后返回
return find_root(father[x]);
}
}
*/
// 查找根节点 - 压缩版
// 让父节点变为根节点,这样就无需多层向上访问父节点了,直接就可以一次访问到根节点
int find_root(int x) {
if (father[x] != x) // 如果x的父节点不是根节点,就要先让x的父节点存为根节点
// 这样的话,每次访问x的父节点的时候,访问的其实都是根节点
father[x] = find_root(father[x]);
return father[x];
}
// 合并集合
void join(int x, int y) {
int rootX = find_root(x);
int rootY = find_root(y);
if (rootX == rootY) return;
father[rootY] = rootX;
}
// 判断x,y是否是同一个集合,也就是是否是同一个根节点
bool isSame(int x, int y) {
return find_root(x) == find_root(y);
}
int main(void) {
return 0;
}
3-卡码网107-寻找存在的路线-并查集-第2次刷题
cpp
// 卡码网=寻找存在的路线
// 算法:DFS BFS 并查集
// Author: 郑龙浩 Date: 2026-02-24
// 这是一个纯模板题
// 将所有的连接的节点都放入同一个集合当中去
#include "bits/stdc++.h"
using namespace std;
// N,M 最大是100,我设为105最大值
const int N = 105;
int father[N];
// 初始化
void init(int n) {
for (int i = 0; i < N; i++) { // 让所有的节点都是根节点
father[i] = i; // 所有节点的父节点都是自己
}
}
// 查找根节点:使用压缩路径的版本
int find_root(int x) {
if (father[x] != x) { // 如果x不是根节点,就要让x的父节点是根节点,直接在最后输出father[x]就行
father[x] = find_root(father[x]); // 让x的父节点是根节点
}
return father[x]; // 相当于返回的根节点,因为x的父节点存储的就是根节点了
}
// 合并节点到一个集合中
void jion (int x, int y) {
int rootX = find_root(x); // x的根节点
int rootY = find_root(y); // y的根节点
if (rootX != rootY) {
father[rootY] = rootX; // 让y节点以及其根节点下的所有节点 整体移到rootX,也就是全部共享rootX一个根节点,方便判断两个节点是否属于同一个集合
}
}
// 判断两个节点是否属于同一个集合
bool isSame(int x, int y) {
if (find_root(x) == find_root(y)) {
return true;
}
return false;
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int n, m;
cin >> n >> m;
init(n); // 初始化数组
int s, t, source, destination;
while (m--) {
cin >> s >> t;
jion(s, t); // 只要s与t之间有连接,也就是两者之间是有边的,就要让s与t处于同一个集合当中
}
cin >> source >> destination;
// 最后判断
// source和destination是否是在同一个集合中的节点
cout << isSame(source, destination);
return 0;
}
4-卡码网108-冗余连接
cpp
// 4-卡码网108-冗余连接.cpp
// Date: 2026-02-24
// Author:郑龙浩
// 算法:并查集
// 思路:
// 既然是在有向图的基础上,去掉一个边的话
// 也就是在输入的过程中我可以使用并查集将所有的点之间的联系记录下来,如果输入的两个点连接后无法变为 有环图(依然是树,也就是会有一个root的),那就让两个点放在同一个集合
// 如果将两点连接之后会变成「有环图」,也就意味着树变成了有环图,此时就可以认定这里两点之间的边就是我们要删除的那个边了
#include "bits/stdc++.h"
using namespace std;
const int N = 1005; // 题目要求N数量最大1000
int father[N];
// 初始化所有节点的父节点为根节点
void init(int n) {
for (int i = 0; i < n; i++) {
father[i] = i;
}
}
// 打印x的根节点 & 将father[x]也设置为根节点(压缩路径之法)
int find_root(int x) {
if (father[x] != x) { // 如果x不是根节点,就要让x的父节点是根节点
father[x] = find_root(father[x]);
}
return father[x];
}
// 将 x 与 y 放入同一个集合中
void join(int x, int y) {
int rootX = find_root(x);
int rootY = find_root(y);
if (rootX != rootY) { // 如果x与y不同一个集合的,就需要合并
father[rootY] = rootX;
}
}
// 判断x y是否在同一个集合
bool isSame(int x, int y) {
return find_root(x) == find_root(y);
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
init(n);
int s, t;
while (n--) {
cin >> s >> t;
// 如果s与t是同一个集合,也就意味着两者是连接的,那么如果再加一个边,就是有环图了,所以直接输出这个边,也就是这两个点,然后退出循环
if (isSame(s, t)) {
cout << s << ' ' << t;
return 0;
} else { // 如果两者不在同一个结合,也就意味着两者现在不是连接的,那么需要join函数将两个节点连接起来
join(s, t);
}
}
return 0;
}