笔记
在涉及二维表中,无论是深搜和广搜,都是定义了一个isUsed数组来标记哪一些元素已经被访问过了,重要的解题思路其实是遍历的条件。
比如孤岛的总面积这一题,判断孤岛就要判断岛屿是否与边界接壤。
dfs深搜
模板-所有可达路径
邻接表法,就记一个邻接表
java
import java.util.*;
public class Main {
// 存储当前路径
static List<Integer> path = new ArrayList<>();
// 存储所有找到的路径
static List<List<Integer>> ret = new ArrayList<>();
public static void main(String[] args) {
// 创建Scanner对象来读取用户输入
Scanner input = new Scanner(System.in);
// 读取顶点数和边数
int n = input.nextInt();
int m = input.nextInt();
// 创建邻接表来表示图,大小为n+1因为顶点编号从1开始
List<List<Integer>> map = new ArrayList<>(m + 1);
for (int i = 0; i <= n; i++) {
map.add(new ArrayList<>());
}
// 读取边的信息,并添加到邻接表中
for (int i = 0; i < m; i++) {
int s = input.nextInt(); // 边的起始顶点
int t = input.nextInt(); // 边的终止顶点
map.get(s).add(t); // 将终止顶点添加到起始顶点的邻接列表中
}
// 将起点(1号顶点)添加到当前路径中,并开始深度优先搜索
path.add(1);
dfs(map, 1, n);
// 检查是否找到了路径
if (ret.isEmpty()) {
System.out.println(-1); // 如果没有找到路径,输出-1
} else {
// 输出所有找到的路径
for (List<Integer> list : ret) {
for (int i = 0; i < list.size() - 1; i++) {
System.out.print(list.get(i) + " ");
}
System.out.println(list.get(list.size() - 1));
}
}
}
// 深度优先搜索方法
static void dfs(List<List<Integer>> map, int i, int n) {
// 如果当前顶点是终点,则将当前路径添加到结果中
if (i == n) {
ret.add(new ArrayList<>(path));
return;
}
// 遍历当前顶点的所有邻接顶点
for (int temp : map.get(i)) {
// 将邻接顶点添加到当前路径中
path.add(temp);
// 递归地对邻接顶点进行深度优先搜索
dfs(map, temp, n);
// 回溯,从当前路径中移除最后一个顶点
path.remove(path.size() - 1);
}
}
}
岛屿数量(深搜版)
java
import java.util.*;
// 主类
public class Main {
// 定义一个静态变量result,用于记录连通区域的数量
static int result = 0;
// 定义一个二维数组dir,表示四个方向(上、右、下、左)的移动
static int dir[][] = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 深度优先搜索(DFS)方法,用于遍历地图上的连通区域
static void dfs(int[][] map, boolean[][] isUsed, int x, int y) {
// 遍历四个方向
for(int i = 0 ; i < 4 ; i++ ){
// 计算下一个位置的坐标
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
// 检查下一个位置是否在地图范围内,并且没有被访问过,以及该位置是否为1(即可以访问)
if(nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length &&
isUsed[nextX][nextY] == false &&
map[nextX][nextY] == 1){
// 标记下一个位置为已访问
isUsed[nextX][nextY] = true;
// 递归调用DFS方法,继续遍历下一个连通区域
dfs(map, isUsed, nextX, nextY);
}
}
}
// 主方法
public static void main (String[] args) {
// 创建一个Scanner对象,用于读取用户输入
Scanner input = new Scanner(System.in);
// 读取地图的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 创建一个二维数组map,用于存储地图信息,1表示可以访问,0表示不可以访问
int map[][] = new int[n][m];
// 读取地图的具体信息
for(int i = 0 ; i < n; i++){
for(int j = 0 ; j < m; j++){
map[i][j] = input.nextInt();
}
}
// 创建一个二维数组isUsed,用于标记地图上的位置是否已经被访问过
boolean isUsed[][] = new boolean[n][m];
// 遍历地图,找出所有未访问的1(可以访问的位置),并使用DFS方法遍历连通区域
for(int i = 0 ; i < n; i++){
for(int j = 0 ; j < m; j++){
// 如果当前位置没有被访问过,并且该位置为1
if(isUsed[i][j] == false && map[i][j] == 1){
// 标记当前位置为已访问
isUsed[i][j] = true;
// 连通区域的数量加1
result++;
// 调用DFS方法,从当前位置开始遍历连通区域
dfs(map, isUsed, i, j);
}
}
}
// 输出连通区域的数量
System.out.println(result);
}
}
岛屿的最大面积(深搜版)
java
import java.util.*;
// 主类
public class Main {
// 定义两个静态变量,分别用于记录找到的最大连通区域的大小和当前连通区域的大小
static int maxSize = 0;
static int localSize = 0;
// 定义一个二维数组dir,表示四个方向(右、下、左、上)的移动
static int dir[][] = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 深度优先搜索(DFS)方法,用于遍历地图上的连通区域
static void dfs(int[][] map, boolean[][] isUsed, int x, int y) {
// 遍历四个方向
for(int i = 0 ; i < 4; i++){
// 计算下一个位置的坐标
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
// 检查下一个位置是否在地图范围内,并且没有被访问过,以及该位置是否为1(即可以访问)
if(nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length &&
isUsed[nextX][nextY] == false &&
map[nextX][nextY] == 1){
// 如果条件满足,将当前位置加入到连通区域中
localSize++;
// 标记下一个位置为已访问
isUsed[nextX][nextY] = true;
// 递归调用DFS方法,继续遍历下一个连通区域
dfs(map, isUsed, nextX, nextY);
}
}
}
// 主方法
public static void main (String[] args) {
// 创建一个Scanner对象,用于读取用户输入
Scanner sc = new Scanner(System.in);
// 读取地图的行数和列数
int m = sc.nextInt();
int n = sc.nextInt();
// 创建一个二维数组map,用于存储地图信息,1表示可以访问,0表示不可以访问
int[][] map = new int[m][n];
// 创建一个二维数组isUsed,用于标记地图上的位置是否已经被访问过
boolean[][] isUsed = new boolean[m][n];
// 读取地图的具体信息
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = sc.nextInt();
}
}
// 遍历地图,找出所有未访问的1(可以访问的位置),并使用DFS方法遍历连通区域
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 如果当前位置没有被访问过,并且该位置为1
if(isUsed[i][j] == false && map[i][j] == 1){
// 标记当前位置为已访问
isUsed[i][j] = true;
// 初始化当前连通区域的大小为1(当前位置)
localSize = 1;
// 调用DFS方法,从当前位置开始遍历连通区域
dfs(map, isUsed, i, j);
// 更新最大连通区域的大小
maxSize = Math.max(maxSize, localSize);
}
}
}
// 输出最大连通区域的大小
System.out.println(maxSize);
}
}
孤岛的总面积(深搜版)
边界问题
java
import java.util.*;
public class Main {
// 用于记录找到的岛屿的总面积
static int size = 0;
// 用于表示四个基本方向:右、下、左、上
// 每个方向用一个包含两个整数的数组表示,第一个整数表示x轴(行)的变化,第二个整数表示y轴(列)的变化
static int dir[][] = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 深度优先搜索(DFS)方法,用于遍历岛屿并计算面积
// map:表示地图的二维数组,其中的元素1表示岛屿,0表示水域
// x:当前位置的x坐标(行)
// y:当前位置的y坐标(列)
static void dfs(int map[][], int x, int y) {
// 将当前位置标记为已访问(0),避免重复计算
map[x][y] = 0;
// 增加岛屿的面积计数
size++;
// 遍历四个方向
for (int i = 0; i < 4; i++) {
// 计算下一个位置的坐标
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
// 检查下一个位置是否在地图范围内,并且是否是未访问的岛屿
if (nextX >= 0 && nextX < map.length && // 检查x坐标是否在范围内
nextY >= 0 && nextY < map[0].length && // 检查y坐标是否在范围内
map[nextX][nextY] == 1) { // 检查该位置是否是岛屿(值为1)
// 如果是未访问的岛屿,递归调用dfs方法
dfs(map, nextX, nextY);
}
}
}
public static void main(String[] args) {
// 创建Scanner对象用于读取用户输入
Scanner input = new Scanner(System.in);
// 读取地图的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 根据输入的行数和列数创建二维数组map
int map[][] = new int[n][m];
// 读取地图的具体数据,1表示岛屿,0表示水域
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = input.nextInt();
}
}
// 遍历地图的边界,寻找岛屿并进行深度优先搜索
// 首先检查上边界和下边界
for (int i = 0; i < n; i++) {
if (map[i][0] == 1) {
// 如果在上边界找到岛屿,调用dfs方法
dfs(map, i, 0);
}
if (map[i][m - 1] == 1) {
// 如果在下边界找到岛屿,调用dfs方法
dfs(map, i, m - 1);
}
}
// 然后检查左边界和右边界
for (int j = 0; j < m; j++) {
if (map[0][j] == 1) {
// 如果在左边界找到岛屿,调用dfs方法
dfs(map, 0, j);
}
if (map[n - 1][j] == 1) {
// 如果在右边界找到岛屿,调用dfs方法
dfs(map, n - 1, j);
}
}
// 重置岛屿的面积计数,因为边界的岛屿可能与内部岛屿相连
size = 0;
// 再次遍历整个地图,寻找剩余的岛屿并进行深度优先搜索
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果找到未访问的岛屿,调用dfs方法
if (map[i][j] == 1) {
dfs(map, i, j);
}
}
}
// 输出找到的岛屿的总面积
System.out.println(size);
}
}
水流问题
边界问题
java
import java.util.*;
public class Main {
// 定义四个方向的移动,分别对应右、下、左、上
static int[][] dir = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 深度优先搜索(DFS)函数,用于遍历地图
static void dfs(int[][] map, boolean[][] isUsed, int x, int y) {
// 标记当前位置已被访问
isUsed[x][y] = true;
// 遍历四个方向
for (int i = 0; i < 4; i++) {
// 计算下一个位置的坐标
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
// 检查下一个位置是否在地图范围内,并且当前位置的高度不大于下一个位置的高度
// 同时确保下一个位置未被访问过
if (nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length &&
map[x][y] <= map[nextX][nextY] &&
isUsed[nextX][nextY] == false) {
// 递归调用DFS函数,继续遍历下一个位置
dfs(map, isUsed, nextX, nextY);
}
}
}
public static void main(String[] args) {
// 创建Scanner对象,用于读取输入
Scanner input = new Scanner(System.in);
// 读取地图的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 创建二维数组,用于存储地图的高度信息
int[][] map = new int[n][m];
// 读取地图的每一行数据
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = input.nextInt();
}
}
// 创建两个二维布尔数组,用于标记从第一组和第二组边界出发可以遍历的节点
boolean[][] firstIsUsed = new boolean[n][m];
boolean[][] secondIsUsed = new boolean[n][m];
// 从最上和最下行的节点出发,向两侧遍历
for (int i = 0; i < n; i++) {
dfs(map, firstIsUsed, i, 0); // 遍历最左列
dfs(map, secondIsUsed, i, m - 1); // 遍历最右列
}
// 从最左和最右列的节点出发,向上下遍历
for (int j = 0; j < m; j++) {
dfs(map, firstIsUsed, 0, j); // 遍历最上行
dfs(map, secondIsUsed, n - 1, j); // 遍历最下行
}
// 遍历整个地图,找出同时被两组边界访问过的节点
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (firstIsUsed[i][j] && secondIsUsed[i][j]) {
System.out.println(i + " " + j); // 输出这些节点的坐标
}
}
}
// 关闭输入流
input.close();
}
}
建造最大岛屿(难)
java
import java.util.*;
public class Main {
// 用于标记不同岛屿的变量
static int mark = 0;
// 用于计数当前岛屿的大小
static int count = 0;
// 定义四个方向的移动,分别对应右、下、左、上
static int[][] dir = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 深度优先搜索(DFS)函数,用于遍历岛屿
static void dfs(int[][] map, boolean[][] isUsed, int x, int y) {
// 标记当前位置已被访问
isUsed[x][y] = true;
// 标记当前位置属于当前岛屿
map[x][y] = mark;
// 增加当前岛屿的大小计数
count++;
// 遍历四个方向
for (int i = 0; i < 4; i++) {
// 计算下一个位置的坐标
int nextX = x + dir[i][0];
int nextY = y + dir[i][1];
// 检查下一个位置是否在地图范围内,并且未被访问过,且当前位置的高度为1(陆地)
if (nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length &&
isUsed[nextX][nextY] == false &&
map[nextX][nextY] == 1) {
// 递归调用DFS函数,继续遍历下一个位置
dfs(map, isUsed, nextX, nextY);
}
}
}
public static void main(String[] args) {
// 创建Scanner对象,用于读取输入
Scanner input = new Scanner(System.in);
// 读取地图的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 创建二维数组,用于存储地图的信息
int[][] map = new int[n][m];
// 读取地图的每一行数据
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = input.nextInt();
}
}
// 标记是否所有位置都是岛屿(陆地)
boolean isAllIsland = true;
// 设置当前岛屿的标记为2,因为1代表陆地
mark = 2;
// 创建二维布尔数组,用于标记每个位置是否被访问过
boolean[][] isUsed = new boolean[n][m];
// 创建HashMap,用于存储每个岛屿的大小
Map<Integer, Integer> getSize = new HashMap<>();
// 遍历整个地图
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果遇到水域(0),则不是所有位置都是岛屿
if (map[i][j] == 0)
isAllIsland = false;
// 如果遇到陆地(1),则进行DFS遍历
if (map[i][j] == 1) {
count = 0; // 重置岛屿大小计数
dfs(map, isUsed, i, j); // 进行DFS遍历
// 将当前岛屿的大小存入HashMap
getSize.put(mark, count);
mark++; // 更新岛屿标记
}
}
}
// 创建HashSet,用于存储已经遍历过的岛屿标记
Set<Integer> containsMark = new HashSet<>();
// 用于存储最大区域的大小
int ret = 0;
// 如果所有位置都是岛屿,则最大区域的大小为整个地图的大小
if (isAllIsland) {
ret = n * m;
}
// 再次遍历整个地图
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果当前位置是水域(0)
if (map[i][j] == 0) {
containsMark.clear(); // 清空HashSet
int localSize = 1; // 当前水域的大小至少为1
// 遍历四个方向
for (int k = 0; k < 4; k++) {
int nextX = i + dir[k][0];
int nextY = j + dir[k][1];
// 检查下一个位置是否在地图范围内
if (nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length) {
int curMark = map[nextX][nextY];
// 如果当前位置未被遍历过,并且HashMap中存在对应的岛屿大小
if (!containsMark.contains(curMark)
&& getSize.containsKey(curMark)) {
localSize += getSize.get(curMark); // 增加当前水域的大小
containsMark.add(curMark); // 将当前岛屿标记加入HashSet
}
}
}
// 更新最大区域的大小
ret = Math.max(localSize, ret);
}
}
}
// 输出最大区域的大小
System.out.println(ret);
}
}
有向图的完全可达性
java
import java.util.*;
// 主类
public class Main {
// 深度优先搜索(DFS)方法,用于遍历图
static void dfs(List<List<Integer>> map, boolean[] isUsed, int s) {
// 如果当前节点已经被访问过,则直接返回
if (isUsed[s] == true) {
return;
}
// 标记当前节点为已访问
isUsed[s] = true;
// 获取当前节点的所有邻接节点
List<Integer> localMap = map.get(s);
// 遍历所有邻接节点,并递归调用DFS方法
for (int num : localMap) {
dfs(map, isUsed, num);
}
}
// 主方法,程序的入口点
public static void main(String[] args) {
// 创建一个Scanner对象,用于从控制台读取输入
Scanner input = new Scanner(System.in);
// 读取顶点的数量
int n = input.nextInt();
// 读取边的数量
int k = input.nextInt();
// 创建一个邻接表,用于表示图
List<List<Integer>> map = new ArrayList<>();
for (int i = 0; i < n; i++) {
// 初始化每个顶点的邻接列表
map.add(new ArrayList<Integer>());
}
// 读取所有的边,并添加到邻接表中
for (int i = 0; i < k; i++) {
int s = input.nextInt(); // 读取边的起始顶点
int t = input.nextInt(); // 读取边的终止顶点
// 将边添加到邻接表中,注意数组是从0开始的,所以需要减1
map.get(s - 1).add(t - 1);
}
// 创建一个布尔数组,用于标记每个顶点是否被访问过
boolean isUsed[] = new boolean[n];
// 从顶点0开始进行深度优先搜索
dfs(map, isUsed, 0);
// 检查是否有未被访问的顶点,如果有,则输出-1并结束程序
for (boolean bool : isUsed) {
if (bool == false) {
System.out.println(-1);
return;
}
}
// 如果所有顶点都被访问过,则输出1
System.out.println(1);
// 关闭Scanner对象
input.close();
}
}
bfs广搜
模板-岛屿数量
java
import java.util.*; // 导入 Java 的集合框架
public class Main {
// 定义四个方向的移动:右、下、左、上
static int[][] dir = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 广度优先搜索 (BFS) 方法
static void bfs(int map[][], int x, int y, boolean isVisited[][]) {
// 使用双端队列来实现 BFS
ArrayDeque<int[]> deque = new ArrayDeque<>();
// 将起始点 (x, y) 加入队列
deque.addFirst(new int[]{x, y});
// 标记起始点为已访问
isVisited[x][y] = true;
// 当队列不为空时,继续进行 BFS
while (!deque.isEmpty()) {
// 从队列中取出当前点
int local[] = deque.removeFirst();
int curX = local[0]; // 当前点的 x 坐标
int curY = local[1]; // 当前点的 y 坐标
// 遍历四个方向
for (int i = 0; i < 4; i++) {
// 计算下一个点的坐标
int nextX = curX + dir[i][0];
int nextY = curY + dir[i][1];
// 检查下一个点是否在地图范围内,未被访问且为 1(可通行)
if (nextX >= 0 && nextX < map.length && nextY >= 0
&& nextY < map[0].length && !isVisited[nextX][nextY] && map[nextX][nextY] == 1) {
// 将可通行的下一个点加入队列
deque.add(new int[]{nextX, nextY});
// 标记下一个点为已访问
isVisited[nextX][nextY] = true;
}
}
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in); // 创建输入扫描器
int n = input.nextInt(); // 读取地图的行数
int m = input.nextInt(); // 读取地图的列数
// 初始化地图,额外多一行和一列以避免边界问题
int map[][] = new int[n + 1][m + 1];
// 读取地图数据
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = input.nextInt(); // 读取每个点的值
}
}
// 初始化访问标记数组
boolean isVisited[][] = new boolean[n + 1][m + 1];
int result = 0; // 记录连通区域的数量
// 遍历整个地图
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果当前点未被访问且为 1,说明发现了一个新的连通区域
if (!isVisited[i][j] && map[i][j] == 1) {
bfs(map, i, j, isVisited); // 从该点开始 BFS
result++; // 计数增加
}
}
}
// 输出连通区域的数量
System.out.println(result);
}
}
岛屿的最大面积(广搜版)
边界问题
java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Scanner;
// 主类
public class Main {
// 定义一个静态变量maxSize,用于记录找到的最大连通区域的大小
static int maxSize = 0;
// 定义一个二维数组dir,表示四个方向(右、下、左、上)的移动
static int dir[][] = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 广度优先搜索(BFS)方法,用于遍历地图上的连通区域
static int bfs(int[][] map, boolean[][] isUsed, int x, int y) {
// 标记当前位置为已访问
isUsed[x][y] = true;
// 初始化当前连通区域的大小为1(当前位置)
int localSize = 1;
// 创建一个双端队列deque,用于实现BFS
Deque<int[]> deque = new ArrayDeque<>();
// 将当前位置加入队列
deque.addFirst(new int[]{x, y});
// 当队列不为空时,继续进行BFS
while(!deque.isEmpty()) {
// 从队列中取出当前点
int local[] = deque.removeLast();
int localx = local[0]; // 当前点的x坐标
int localy = local[1]; // 当前点的y坐标
// 遍历四个方向
for(int i = 0 ; i < 4 ; i++ ) {
// 计算下一个点的坐标
int nextX = localx + dir[i][0];
int nextY = localy + dir[i][1];
// 检查下一个点是否在地图范围内,未被访问且为1(可通行)
if(nextX >= 0 && nextX < map.length &&
nextY >= 0 && nextY < map[0].length &&
isUsed[nextX][nextY] == false &&
map[nextX][nextY] == 1) {
// 将可通行的下一个点加入队列
isUsed[nextX][nextY] = true;
deque.addLast(new int[]{nextX, nextY});
// 更新当前连通区域的大小
localSize++;
}
}
}
// 返回当前连通区域的大小
return localSize;
}
// 主方法
public static void main (String[] args) {
// 创建一个Scanner对象,用于读取用户输入
Scanner input = new Scanner(System.in);
// 读取地图的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 创建一个二维数组map,用于存储地图信息,1表示可以访问,0表示不可以访问
int[][] map = new int[n][m];
// 读取地图的具体信息
for(int i = 0 ; i < n; i++) {
for(int j = 0 ; j < m; j++) {
map[i][j] = input.nextInt();
}
}
// 创建一个二维数组isUsed,用于标记地图上的位置是否已经被访问过
boolean[][] isUsed = new boolean[n][m];
// 遍历地图,找出所有未访问的1(可以访问的位置),并使用BFS方法遍历连通区域
for(int i = 0 ; i < n; i++) {
for(int j = 0 ; j < m; j++) {
// 如果当前位置没有被访问过,并且该位置为1
if(isUsed[i][j] == false && map[i][j] == 1) {
// 调用BFS方法,从当前位置开始遍历连通区域
int localSize = bfs(map, isUsed, i, j);
// 更新最大连通区域的大小
maxSize = Math.max(localSize, maxSize);
}
}
}
// 输出最大连通区域的大小
System.out.println(maxSize);
}
}
字符串接龙(无向图!)
java
import java.util.*;
public class Main {
// 使用广度优先搜索(BFS)算法计算从一个字符串到另一个字符串的最少步数
static int bfs(String str[], Set<String> strList) {
// 初始化起始字符串和结束字符串
String beginStr = str[0];
String endStr = str[1];
// 使用双端队列来存储待处理的字符串
Deque<String> deque = new ArrayDeque<>();
// 将起始字符串加入队列
deque.add(beginStr);
// 使用HashMap来存储每个字符串到起始字符串的步数
Map<String, Integer> path = new HashMap<>();
// 设置起始字符串的步数为0
path.put(beginStr, 1);
// 开始广度优先搜索
while (!deque.isEmpty()) {
// 从队列中取出一个字符串
String curStr = deque.removeLast();
// 获取当前字符串到起始字符串的步数
int count = path.get(curStr);
// 遍历当前字符串的每个字符
for (int i = 0; i < curStr.length(); i++) {
// 将当前字符串转换为字符数组
char[] curStrArray = curStr.toCharArray();
// 遍历所有小写字母
for (char ch = 'a'; ch <= 'z'; ch++) {
// 替换当前位置的字符
curStrArray[i] = ch;
// 将字符数组转换回字符串
String newStr = String.valueOf(curStrArray);
// 如果新字符串等于结束字符串,返回步数
if (newStr.equals(endStr)) {
return count + 1;
}
// 如果新字符串在字符串列表中,并且没有被处理过
if (strList.contains(newStr) && !path.containsKey(newStr)) {
// 将新字符串加入队列
deque.addFirst(newStr);
// 记录新字符串到起始字符串的步数
path.put(newStr, count + 1);
}
}
}
}
// 如果没有找到结束字符串,返回0
return 0;
}
public static void main(String[] args) {
// 创建Scanner对象,用于读取输入
Scanner input = new Scanner(System.in);
// 读取字符串的数量
int n = input.nextInt();
// 读取下一行,忽略换行符
input.nextLine();
// 读取起始和结束字符串
String BEStr = input.nextLine();
// 将起始和结束字符串分割并存储在数组中
String[] str = BEStr.split(" ");
// 创建HashSet,用于存储字符串列表
Set<String> strList = new HashSet<>();
// 读取n个字符串并加入HashSet
for (int i = 0; i < n; i++) {
strList.add(input.nextLine());
}
// 调用bfs函数,计算步数
int ret = bfs(str, strList);
// 输出结果
System.out.println(ret);
}
}
非深搜广搜的其他类型题目
岛屿的周长
java
import java.util.*;
public class Main {
public static void main(String[] args) {
// 创建一个 Scanner 对象来读取用户的输入
Scanner input = new Scanner(System.in);
// 读取用户输入的行数和列数
int n = input.nextInt();
int m = input.nextInt();
// 创建一个二维数组来存储地图信息,初始值都为0
int map[][] = new int[n][m];
// 读取地图信息,填充到二维数组中
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = input.nextInt();
}
}
// 关闭 Scanner 对象,释放资源
input.close();
// 定义四个方向的移动,分别是右、下、左、上
int dir[][] = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
// 初始化返回值,用于计数
int ret = 0;
// 遍历地图的每个格子
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果当前格子的值为1,表示是一个陆地格子
if (map[i][j] == 1) {
// 遍历四个方向
for (int k = 0; k < 4; k++) {
// 计算下一个格子的坐标
int nextX = i + dir[k][0];
int nextY = j + dir[k][1];
// 检查下一个格子是否在地图范围内,并且不是陆地(值为0)
// 注意:这里使用 map.length 和 map[0].length 来获取行数和列数
if (nextX < 0 || nextX >= map.length ||
nextY < 0 || nextY >= map[0].length ||
map[nextX][nextY] == 0) {
ret++;
}
}
}
}
}
// 输出结果,即周围不是陆地的格子数
System.out.println(ret);
}
}
并查集
理论在其他文章
寻找存在的路径
无向图的路径问题
java
import java.util.*;
public class Main {
// 定义并查集的大小
static int n = 101;
// 用于存储每个元素的父节点,初始时每个元素的父节点都是自己
static int father[] = new int[n];
// 初始化并查集
static void init() {
for (int i = 0; i < n; i++) {
father[i] = i; // 每个元素的父节点设置为自己
}
}
// 查找元素u的根节点
static int find(int u) {
return u == father[u] ? u : find(father[u]); // 如果u的父节点是它自己,说明u就是根节点
}
// 合并元素u和v所在的集合
static void join(int u, int v) {
u = find(u); // 找到u的根节点
v = find(v); // 找到v的根节点
if (u == v) {
return; // 如果u和v已经在同一个集合中,不需要合并
}
father[v] = u; // 将v的根节点设置为u,这样v所在的集合就被合并到了u所在的集合中
}
// 判断元素u和v是否在同一个集合中
static boolean isSame(int u, int v) {
u = find(u); // 找到u的根节点
v = find(v); // 找到v的根节点
return u == v; // 如果根节点相同,则u和v在同一个集合中
}
public static void main(String[] args) {
// 初始化并查集
init();
// 创建Scanner对象用于读取输入
Scanner input = new Scanner(System.in);
// 读取测试用例的数量
int n = input.nextInt();
// 读取操作的数量
int m = input.nextInt();
// 处理每个操作
for (int i = 0; i < m; i++) {
int u = input.nextInt(); // 读取操作中的第一个元素
int v = input.nextInt(); // 读取操作中的第二个元素
join(u, v); // 合并u和v所在的集合
}
// 读取源点和目标点
int source = input.nextInt();
int destination = input.nextInt();
// 判断源点和目标点是否在同一个集合中
if (isSame(source, destination)) {
System.out.println(1); // 如果在同一个集合中,输出1
} else {
System.out.println(0); // 如果不在同一个集合中,输出0
}
// 关闭Scanner对象
input.close();
}
}
冗余连接(无向图)
java
import java.util.*;
public class Main {
// 定义并查集的最大元素数量
static int N = 1001;
// 用于存储每个元素的父节点,初始时每个元素的父节点都是自己
static int father[] = new int[N];
// 初始化并查集
static void init() {
for (int i = 0; i < N; i++) {
father[i] = i; // 每个元素的父节点设置为自己
}
}
// 查找元素u的根节点
static int find(int u) {
return u == father[u] ? u : find(father[u]); // 如果u的父节点是它自己,说明u就是根节点
}
// 合并元素u和v所在的集合
static void join(int u, int v) {
u = find(u); // 找到u的根节点
v = find(v); // 找到v的根节点
if (u == v) {
return; // 如果u和v已经在同一个集合中,不需要合并
}
father[v] = u; // 将v的根节点设置为u,这样v所在的集合就被合并到了u所在的集合中
}
// 判断元素u和v是否在同一个集合中
static boolean isSame(int u, int v) {
u = find(u); // 找到u的根节点
v = find(v); // 找到v的根节点
return u == v; // 如果根节点相同,则u和v在同一个集合中
}
public static void main(String[] args) {
// 初始化并查集
init();
// 创建Scanner对象用于读取输入
Scanner input = new Scanner(System.in);
// 读取操作的数量
int n = input.nextInt();
// 处理每个操作
for (int i = 0; i < n; i++) {
int s = input.nextInt(); // 读取操作中的第一个元素
int t = input.nextInt(); // 读取操作中的第二个元素
// 判断s和t是否已经在同一个集合中
if (isSame(s, t)) {
System.out.println(s + " " + t); // 如果已经在同一个集合中,输出这对元素并结束程序
return;
} else {
join(s, t); // 如果不在同一个集合中,将它们所在的集合合并
}
}
// 关闭Scanner对象
input.close();
}
}
冗余连接II(有向图)
java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
// 用于存储每个节点的父节点,初始化时每个节点的父节点都是自己
static int father[] = new int[1001];
// 初始化每个节点的父节点
static void init() {
for (int i = 0; i < 1001; i++) {
father[i] = i;
}
}
// 查找节点u的根节点,使用路径压缩
static int find(int u) {
return u == father[u] ? u : find(father[u]);
}
// 判断两个节点是否在同一个集合中(是否有相同的根节点)
static boolean isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 将节点u和v合并到同一个集合中
static void join(int u, int v) {
u = find(u);
v = find(v);
if (u == v)
return;
father[v] = u;
}
// 检测移除指定边后是否能够形成一棵树
static boolean isTreeAfterRemoveEdge(int deleteEdge, int map[][]) {
init();
// 遍历所有边,跳过要删除的边
for (int i = 1; i < map.length; i++) {
if (deleteEdge == i)
continue;
// 如果两个节点已经在同一个集合中,则不能形成树
if (isSame(map[i][0], map[i][1])) {
return false;
} else {
// 将两个节点合并到同一个集合中
join(map[i][0], map[i][1]);
}
}
// 如果所有边都能合并,则可以形成树
return true;
}
// 检测图中是否存在环
static void isCircle(int map[][]) {
init();
// 遍历所有边,如果发现两个节点已经在同一个集合中,则输出这条边并返回
for (int i = 1; i < map.length; i++) {
if (isSame(map[i][0], map[i][1])) {
System.out.println(map[i][0] + " " + map[i][1]);
return;
} else {
join(map[i][0], map[i][1]);
}
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt(); // 读取边的数量
// 存储每个节点的度数
int degree[] = new int[n + 1];
// 存储边的信息,map[i][0]和map[i][1]分别表示第i条边的两个节点
int map[][] = new int[n + 1][2];
// 读取边的信息
for (int i = 1; i <= n; i++) {
int u = input.nextInt();
int v = input.nextInt();
map[i][0] = u;
map[i][1] = v;
degree[v]++;
}
// 存储度数为2的边的索引
List<Integer> twoDegree = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if (degree[map[i][1]] == 2) {
twoDegree.add(i);
}
}
// 如果存在度数为2的节点
if (!twoDegree.isEmpty()) {
int firstEdge = twoDegree.get(twoDegree.size() - 1);
// 尝试移除最后一条度数为2的边,检测是否能够形成树
if (isTreeAfterRemoveEdge(firstEdge, map)) {
System.out.println(map[firstEdge][0] + " " + map[firstEdge][1]);
} else {
// 如果不能形成树,则尝试移除倒数第二条度数为2的边
System.out.println(map[twoDegree.get(twoDegree.size() - 2)][0] + " "
+ map[twoDegree.get(twoDegree.size() - 2)][1]);
}
} else {
// 如果不存在度数为2的节点,则检测图中是否存在环
isCircle(map);
}
}
}