110.字符串接龙
思路:
这道题要解决两个问题:
- 图中的线是如何连在一起的:判断点与点之间的关系,需要判断是不是差一个字符,如果差一个字符,那就是有链接。
- 起点和终点的最短路径长度:无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径。
注意点:
- 本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环!
- 使用set来检查字符串是否出现在字符串集合里更快一些
题解:
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 读取字符串集合的大小
String beginStr = scanner.next(); // 读取初始字符串
String endStr = scanner.next(); // 读取目标字符串
HashSet<String> wordSet = new HashSet<>(); // 字符串集合,使用HashSet存储以便于快速查找
for (int i = 0; i < n; i++) {
wordSet.add(scanner.next()); // 将输入的字符串添加到集合中
}
HashSet<String> visitedSet = new HashSet<>(); // 记录已经访问过的字符串
Deque<String> deque = new LinkedList<>(); // 双端队列用于BFS
deque.addLast(beginStr); // 将初始字符串加入队列
// 初始化visitedSet
visitedSet.add(beginStr); // 将初始字符串标记为已访问
// 开始BFS搜索
int steps = 1; // 初始字符串的转换步骤数为1
while (!deque.isEmpty()) {
int size = deque.size(); // 当前层的节点数
for (int k = 0; k < size; k++) {
String word = deque.pollFirst(); // 从队列头部取出当前字符串
// 遍历当前字符串的每个字符位置
for (int i = 0; i < word.length(); i++) {
char[] chars = word.toCharArray(); // 将字符串转为字符数组,便于逐字符修改
// 尝试将当前字符替换为'a'到'z'的所有字符
for (char j = 'a'; j <= 'z'; j++) {
chars[i] = j; // 替换字符
String newWord = new String(chars); // 形成新的字符串
if (newWord.equals(word)) {
continue; // 如果新字符串和当前字符串相同,则跳过
}
if (newWord.equals(endStr)) {
System.out.println(steps + 1); // 如果新字符串是目标字符串,则输出结果
return;
}
// 如果新字符串在字符串集合中且未被访问过
if (wordSet.contains(newWord) && !visitedSet.contains(newWord)) {
visitedSet.add(newWord); // 记录新字符串为已访问
deque.addLast(newWord); // 将新字符串加入队列
}
}
}
}
steps++; // 每遍历完一层,步数加一
}
}
}
105.有向图的完全可达性
题目链接:105.有向图的完全可达性
文档讲解:代码随想录
状态:不会
思路:
- 先用邻接矩阵或者邻接表存放图的关系
- 使用dfs或者bfs遍历临界矩阵或邻接表
题解:
public class Main {
public static void main(String[] args) {
// 创建Scanner对象以读取输入
Scanner scanner = new Scanner(System.in);
// 读取顶点数量n和边数量k
int n = scanner.nextInt();
int k = scanner.nextInt();
// 初始化图的邻接矩阵
int[][] graph = new int[n + 1][n + 1];
// 初始化访问数组
boolean[] visited = new boolean[n + 1];
// 读取每条边并在邻接矩阵中记录
for (int i = 0; i < k; i++) {
int from = scanner.nextInt();
int to = scanner.nextInt();
graph[from][to] = 1;
}
// 从节点1开始进行深度优先搜索
dfs(graph, visited, 1, n);
// 检查所有节点是否都被访问过
for (int i = 1; i < visited.length; i++) {
if (!visited[i]) {
// 如果有节点未被访问过,则输出-1
System.out.println(-1);
return;
}
}
// 如果所有节点都被访问过,则输出1
System.out.println(1);
}
// 深度优先搜索函数
public static void dfs(int[][] graph, boolean[] visited, int node, int n) {
if (visited[node]) {
return;
}
// 将当前节点标记为已访问
visited[node] = true;
// 遍历所有可能的下一个节点
for (int i = 1; i <= n; i++) {
// 如果可以从节点1-->i,就继续递归遍历能否从i到n
// 如果节点未被访问且存在从当前节点到该节点的边
if (!visited[i] && graph[node][i] == 1) {
// 递归进行深度优先搜索
dfs(graph, visited, i, n);
}
}
}
}
106.岛屿的周长
思路:这道题不用dfs,bfs。。。
遍历每一个空格,遇到岛屿则计算其上下左右的空格情况。
如果该陆地上下左右的空格是有水域,则说明是一条边,如果该陆地上下左右的空格出界了,说明也是一条边。
题解:
public class Main {
// 定义方向数组,表示右,下,左,上四个方向
public static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
public static void main(String[] args) {
// 创建Scanner对象以读取输入
Scanner scanner = new Scanner(System.in);
// 读取网格的行数n和列数m
int n = scanner.nextInt();
int m = scanner.nextInt();
// 初始化网格
int[][] grid = new int[n][m];
// 读取网格中的值
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
grid[i][j] = scanner.nextInt();
}
}
// 记录岛屿的周长
int res = 0;
// 遍历网格中的每个单元格
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果当前单元格是陆地
if (grid[i][j] == 1) {
// 检查当前单元格的四个方向
for (int k = 0; k < 4; k++) {
int x = i + dir[k][0];
int y = j + dir[k][1];
// 如果当前单元格在边界上,或周围是水,则周长加1
if (x < 0 || x >= n || y < 0 || y >= m || grid[x][y] == 0) {
res++;
}
}
}
}
}
// 输出岛屿的周长
System.out.println(res);
}
}