题目描述
字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:
给你两个字符串 beginStr 和 endStr 和一个字典 strList,找到从 beginStr 到 endStr 的最短转换序列中的字符串数目。如果不存在这样的转换序列,返回 0。
输入描述
第一行包含一个整数 N,表示字典 strList 中的字符串数量。 第二行包含两个字符串,用空格隔开,分别代表 beginStr 和 endStr。 后续 N 行,每行一个字符串,代表 strList 中的字符串。
输出描述
输出一个整数,代表从 beginStr 转换到 endStr 需要的最短转换序列中的字符串数量。如果不存在这样的转换序列,则输出 0。
输入示例
6
abc def
efc
dbc
ebc
dec
dfc
yhn
输出示例
4
提示信息
从 startStr 到 endStr,在 strList 中最短的路径为 abc -> dbc -> dec -> def,所以输出结果为 4
数据范围:
2 <= N <= 500
import java.util.*;
public class Main {
public static void main(String[] args) {
//输入
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//单词列表中可用的单词个数
scanner.nextLine();//换行
String beginStr = scanner.next();//起始单词
String endStr = scanner.next();//目标单词
scanner.nextLine();//换行
//初始化存储所有的单词
List wordList = new ArrayList<>();
//将2个转换词组加进去
wordList.add(beginStr);
wordList.add(endStr);
//读取n个单词到wordList中
for (int i = 0; i < n; i++) {
wordList.add(scanner.nextLine());
}
//bfs
int count = bfs(beginStr, endStr, wordList);
System.out.println(count);
}
/**
* 广度优先搜索-寻找最短路径
*/
public static int bfs(String beginStr, String endStr, List wordList) {
int len = 1;//路径的层数,初始路径长度为1
//1.set:判断单词是否在wordList里[查询]
Set set = new HashSet<>(wordList);
//2.防止重复访问的单词集合
Set visited = new HashSet<>();
//3.存储bfs层次遍历的队列[beginstr]
Queue q = new LinkedList<>();
visited.add(beginStr);
q.add(beginStr);
q.add(null);
//4.每次修改1个字母,看看是否变成set里的单词
//6.如果队列为null,还没找到endStr,返回0
//bfs层次遍历
//区分不同的层次,确保计算的是最短路径的步数。
//null是层次标记,来标记当前层结束,进入下一层
while (!q.isEmpty()) {
String node = q.remove();//取出beginstr头部单词
//遇到null,这一层结束
if (node == null) {
//if q不为空,还有下一层的单词需要处理,增加len(层数+1)
if (!q.isEmpty()) {
len++;//进入下一层
q.add(null);//在队列尾部加入null作为下一层的标记
}
continue;//继续下一次循环
}
//寻找邻接单词
//改变单词的一个字母,寻找邻接单词,用bfs找出beginstr到endStr的最短路径
//(1)遍历当前单词node的每个字符
char[] charArray = node.toCharArray();
//(2)寻找邻接单词
for (int i = 0; i < charArray.length; i++) {
//记录旧值,用于回滚修改
char old = charArray[i];
//(3)改变node的每个字符,用a-z26个字母替换charArray[i],生成新单词newWord
for (char j = 'a'; j <= 'z'; j++) {
charArray[i] = j;
String newWord = new String(charArray);
//(4)检查newWord是否在wordList里
//如果newWord在set里,并且没被访问过
if (set.contains(newWord) && !visited.contains(newWord)) {
q.add(newWord);//加入队列,等待下一轮bfs
visited.add(newWord);//记录访问,防止重复
//找到结尾
//(5)如果newWord是endStr,返回len+1最短路径长度
if (newWord.equals(endStr)) return len + 1;
}
}
//4.恢复原始单词
charArray[i] = old;
}
}
return 0;
}
}
【题目描述】
给定一个有向图,包含 N 个节点,节点编号分别为 1,2,…,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。
【输入描述】
第一行包含两个正整数,表示节点数量 N 和边的数量 K。 后续 K 行,每行两个正整数 s 和 t,表示从 s 节点有一条边单向连接到 t 节点。
【输出描述】
如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。
【输入示例】
4 4
1 2
2 1
1 3
2 4
【输出示例】
1
【提示信息】
从 1 号节点可以到达任意节点,输出 1。
数据范围:
思路
有向图搜索全路径,只能用深搜(dfs)和广搜(bfs)来搜
深搜三部曲
处理当前访问的节点还是处理下一个要访问的节点,决定终止条件怎么写
什么是处理?
2种情况
处理当前节点,有终止条件
// 写法一:处理当前访问的节点
void dfs(const vector>& graph, int key, vector& visited) {
if (visited[key]) {
return;
}
visited[key] = true;
list keys = graph[key];
for (int key : keys) {
// 深度优先搜索遍历
dfs(graph, key, visited);
}
}
看作是处理下一个节点,没有终止条件
// 写法二:处理下一个要访问的节点
void dfs(const vector>& graph, int key, vector& visited) {
list keys = graph[key];
for (int key : keys) {
if (visited[key] == false) { // 确认下一个是没访问过的节点
visited[key] = true;
dfs(graph, key, visited);
}
}
}
核心思路
dfs
bfs
代码1 dfs
import java.util.*;
public class Main{
//1.构建邻接表adjList(图的结构)
public static List> adjList=new ArrayList<>();
public static void main(String[] agrs){
//输入
Scanner sc=new Scanner(System.in);
int vertices_num=sc.nextInt();
int line_num=sc.nextInt();
//装载到LinkedList
for(int i=0;i());
}
//初始化
for(int i=0;i nextKeys=adjList.get(key);
for(int nextKey:nextKeys){
dfs(visited,nextKey);
}
}
}
代码2
import java.util.*;
public class Main{
//1.构建邻接表adjList(图的结构)
public static List> adjList=new ArrayList<>();
public static void main(String[] agrs){
//输入
Scanner sc=new Scanner(System.in);
int vertices_num=sc.nextInt();
int line_num=sc.nextInt();
//装载到LinkedList
for(int i=0;i());
}
//初始化
for(int i=0;i nextKeys=adjList.get(key);//从1号节点(索引0)开始
for(int nextKey:nextKeys){
dfs(visited,nextKey);
}
}
//3.bfs
public static void bfs(boolean[] visited,int key){
Queue queue=new LinkedList();
queue.add(key);//从1号节点索引0开始
visited[key]=true;//标记起点已访问
while(!queue.isEmpty()){
int curKey=queue.poll();//取出当前节点
List list=adjList.get(curKey);//获得邻接点
for(int nextKey:list){
//只访问未访问的节点
if(!visited[nextKey]){
queue.add(nextKey);//加入队列,等到下一轮遍历
visited[nextKey]=true;//标记已访问
}
}
}
}
}
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。
你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算岛屿的周长。岛屿内部没有水域。
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。
输出一个整数,表示岛屿的周长。
5 5
0 0 0 0 0
0 1 0 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0
14
岛屿的周长为 14。
数据范围:
1 <= M, N <= 50。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int M = sc.nextInt();
int[][] grid = new int[N][M];
// 读取输入
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
grid[i][j] = sc.nextInt();
}
}
// 计算岛屿周长
System.out.println(islandPerimeter(grid));
}
public static int islandPerimeter(int[][] grid) {
int perimeter = 0;
int rows = grid.length, cols = grid[0].length;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (grid[i][j] == 1) {
perimeter += 4; // 每个陆地默认贡献4条边
// 上方有陆地
if (i > 0 && grid[i - 1][j] == 1) perimeter -= 2;
// 左方有陆地
if (j > 0 && grid[i][j - 1] == 1) perimeter -= 2;
}
}
}
return perimeter;
}
}