Day47 | 110.字符串接龙 105.有向图的完全可达性 106.岛屿的周长

ops/2024/9/22 21:41:29/

110.字符串接龙

110. 字符串接龙

题目

题目描述

字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列: 

1. 序列中第一个字符串是 beginStr。

2. 序列中最后一个字符串是 endStr。 

3. 每次转换只能改变一个字符。 

4. 转换过程中的中间字符串必须是字典 strList 中的字符串,且strList里的每个字符串只用使用一次。 

给你两个字符串 beginStr 和 endStr 和一个字典 strList,找到从 beginStr 到 endStr 的最短转换序列中的字符串数目。如果不存在这样的转换序列,返回 0。

输入描述

第一行包含一个整数 N,表示字典 strList 中的字符串数量。 第二行包含两个字符串,用空格隔开,分别代表 beginStr 和 endStr。 后续 N 行,每行一个字符串,代表 strList 中的字符串。

输出描述

输出一个整数,代表从 beginStr 转换到 endStr 需要的最短转换序列中的字符串数量。如果不存在这样的转换序列,则输出 0。

思路

  1. 初始化
    • 使用HashSetset)来存储wordList中的所有单词,以便快速检查某个单词是否存在于列表中。
    • 使用Queuequeue)来存储待处理的单词,这些单词是当前已经找到但尚未探索完所有可能变化的单词。
    • 使用HashMapvisitMap)来记录每个单词被访问时的路径长度(即距离起始单词的步数)。
  2. BFS过程
    • 将起始单词加入队列和访问记录中,并设置其路径长度为1。
    • 循环处理队列中的单词,直到队列为空。
    • 对于每个单词,尝试替换其每个位置的字符为'a'到'z'之间的所有字符,生成新的单词。
    • 检查新单词是否为目标单词,如果是,则返回当前路径长度加1。
    • 如果新单词存在于set中且之前未被访问过,则将其加入队列和访问记录中,并更新其路径长度。
  3. 结果
    • 如果遍历完所有可能的单词后仍未找到目标单词,则返回0,表示无法从起始单词到达目标单词。

代码

java">import java.util.*;public class Main {// BFS方法public static int ladderLength(String beginWord, String endWord, List<String> wordList) {// 使用set作为查询容器,效率更高HashSet<String> set = new HashSet<>(wordList);// 声明一个queue存储每次变更一个字符得到的且存在于容器中的新字符串Queue<String> queue = new LinkedList<>();// 声明一个hashMap存储遍历到的字符串以及所走过的路径pathHashMap<String, Integer> visitMap = new HashMap<>();queue.offer(beginWord);visitMap.put(beginWord, 1);while (!queue.isEmpty()) {String curWord = queue.poll();int path = visitMap.get(curWord);for (int i = 0; i < curWord.length(); i++) {char[] ch = curWord.toCharArray();// 每个位置尝试26个字母for (char k = 'a'; k <= 'z'; k++) {ch[i] = k;String newWord = new String(ch);if (newWord.equals(endWord)) return path + 1;// 如果这个新字符串存在于容器且之前未被访问到if (set.contains(newWord) && !visitMap.containsKey(newWord)) {visitMap.put(newWord, path + 1);queue.offer(newWord);}}}}return 0;}public static void main (String[] args) {/* code */// 接收输入Scanner sc = new Scanner(System.in);int N = sc.nextInt();sc.nextLine();String[] strs = sc.nextLine().split(" ");List<String> wordList = new ArrayList<>();for (int i = 0; i < N; i++) {wordList.add(sc.nextLine());}// wordList.add(strs[1]);// 打印结果int result = ladderLength(strs[0], strs[1], wordList);System.out.println(result);}
}

易错点

  1. 输入处理
    • 在读取N(单词列表的大小)后,需要调用sc.nextLine()来跳过行尾的换行符,否则在读取第一个单词时会出错。
    • 题目中可能未明确说明,但通常假设strs[0]是起始单词,strs[1]是目标单词,而剩余的单词在wordList中。然而,在你的代码中,你直接从sc.nextLine()读取了所有单词(除了N),这可能不是题目意图。你应该根据题目要求来读取这些单词。
  2. 字符串处理
    • 在替换字符时,需要确保使用字符数组来避免在循环中多次创建新的字符串对象。
    • 替换字符后,应检查新生成的单词是否存在于set中且之前未被访问过。
  3. 边界条件
    • 如果wordList为空或未包含起始单词和目标单词,应返回适当的值(通常是0,表示无法到达)。
    • 如果起始单词和目标单词相同,应返回1(因为不需要任何变化)。

105.有向图的完全可达性

105. 有向图的完全可达性

题目

题目描述

给定一个有向图,包含 N 个节点,节点编号分别为 1,2,...,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

输入描述

第一行包含两个正整数,表示节点数量 N 和边的数量 K。 后续 K 行,每行两个正整数 s 和 t,表示从 s 节点有一条边单向连接到 t 节点。

输出描述

如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

思路

  1. 输入处理:首先,程序通过Scanner类读取图的节点数n和边数m。然后,它初始化一个大小为n+1List<List<Integer>>类型的邻接表graph,其中每个内部列表代表一个节点的所有邻居节点。由于节点编号从1开始,但数组索引从0开始,因此需要大小为n+1的列表数组。

  2. 构建邻接表:通过读取每对相连的节点st,将t添加到s的邻居列表中。注意,这里的st是节点编号,不是索引,因此它们可以直接用作graph的索引(尽管实际上它们被用作索引时减1,因为内部索引是从0开始的)。

  3. 深度优先搜索:从节点1开始进行深度优先搜索。在搜索过程中,使用一个布尔数组visited来跟踪哪些节点已经被访问过。对于每个节点,如果它尚未被访问,则标记为已访问,并递归地访问其所有邻居。

  4. 检查访问情况:在DFS完成后,程序遍历visited数组,检查是否有任何节点未被访问。如果有任何节点未被访问,则输出-1表示不是所有节点都可从起始节点访问;如果所有节点都被访问,则输出1

代码

java">import java.util.ArrayList;  
import java.util.Arrays;  
import java.util.List;  
import java.util.Scanner;  public class Main {  static void dfs(List<List<Integer>> graph, int key, boolean[] visited) {  if (visited[key]) {  return;  }  visited[key] = true;  List<Integer> keys = graph.get(key);  for (int neighbor : keys) {  // 深度优先搜索遍历  dfs(graph, neighbor, visited);  }  }  public static void main(String[] args) {  Scanner scanner = new Scanner(System.in);  int n = scanner.nextInt();  int m = scanner.nextInt();  // 节点编号从1到n,但数组索引从0开始,所以申请 n+1 这么大的数组  List<List<Integer>> graph = new ArrayList<>(n + 1);  for (int i = 0; i <= n; i++) {  graph.add(new ArrayList<>());  }  while (m-- > 0) {  int s = scanner.nextInt();  int t = scanner.nextInt();  // 使用邻接表 ,表示 s -> t 是相连的  graph.get(s).add(t);  }  boolean[] visited = new boolean[n + 1];  dfs(graph, 1, visited);  // 检查是否都访问到了  boolean allVisited = true;  for (int i = 1; i <= n; i++) {  if (!visited[i]) {  System.out.println(-1);  allVisited = false;  break;  }  }  if (allVisited) {  System.out.println(1);  }  scanner.close();  }  
}

易错点

  1. 索引与节点编号的混淆:在这个问题中,节点编号是从1开始的,但数组索引是从0开始的。这可能导致在处理输入时出错,尤其是当直接使用节点编号作为索引时。然而,在这个特定的代码中,由于graph的索引被正确地处理(即graph.get(s)中的s是节点编号,但内部自动减1以用作索引),这个错误被避免了。

  2. 邻接表的构建:在构建邻接表时,必须确保每个节点都有一个对应的空列表。在这个例子中,通过初始化一个大小为n+1ArrayList<ArrayList<Integer>>来确保这一点。

  3. DFS的递归深度:如果图是高度递归的(例如,存在很长的路径或循环),那么DFS可能会导致堆栈溢出。虽然在这个简单的例子中不太可能出现,但在处理大型或复杂的图时需要注意。

106.岛屿的周长

106. 岛屿的周长

题目

题目描述

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。

你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算岛屿的周长。岛屿内部没有水域。

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

输出一个整数,表示岛屿的周长。

思路

目的是计算一个二维网格中所有值为1的单元格的周长总和。程序首先通过标准输入接收网格的行数M和列数N,然后读取网格的具体内容(即每个单元格的值)。接下来,程序遍历整个网格,对于每个值为1的单元格,它调用helper函数来计算该单元格的周长,并将所有值为1的单元格的周长累加到result变量中。最后,程序输出总周长。

代码

java">import java.util.*;public class Main {// 每次遍历到1,探索其周围4个方向,并记录周长,最终合计// 声明全局变量,dirs表示4个方向static int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};// 统计每单个1的周长static int count;// 探索其周围4个方向,并记录周长public static void helper(int[][] grid, int x, int y) {for (int[] dir : dirs) {int nx = x + dir[0];int ny = y + dir[1];// 遇到边界或者水,周长加一if (nx < 0 || nx >= grid.length || ny < 0 || ny >= grid[0].length|| grid[nx][ny] == 0) {count++;}}}public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 接收输入int M = sc.nextInt();int N = sc.nextInt();int[][] grid = new int[M][N];for (int i = 0; i < M; i++) {for (int j = 0; j < N; j++) {grid[i][j] = sc.nextInt();}}int result = 0; // 总周长for (int i = 0; i < M; i++) {for (int j = 0; j < N; j++) {if (grid[i][j] == 1) {count = 0;helper(grid, i, j);// 更新总周长result += count;}}}// 打印结果System.out.println(result);}
}

易错点

  1. 全局变量count的线程安全问题
    在这个程序中,count被用作全局变量来存储单个值为1的单元格的周长。由于Java的static变量是类级别的,而不是实例级别的,因此它在这个上下文中是安全的,因为程序是单线程的。然而,在多线程环境中,这种使用全局变量的方式可能会导致竞态条件。在这个特定程序中,由于不存在多线程,所以这不是一个问题。

  2. 边界条件处理
    helper函数正确地处理了边界条件和值为0的单元格,这是计算周长所必需的。然而,如果网格的大小(MN)为0或负数,程序将抛出异常。虽然这种情况在常规输入中不太可能发生,但最好添加一些额外的检查来确保输入的有效性。

总结

明天继续图论

继续加油

成功的人不是赢在起点,而是坚持到终点


http://www.ppmy.cn/ops/99309.html

相关文章

【ARM 芯片 安全与攻击 5.4 -- Meltdown 攻击与防御介绍】

文章目录 什么是 Meltdown 攻击?Meltdown 攻击的基本原理Meltdown 攻击代码示例Meltdown 攻击在芯片中的应用应用场景Meltdown 攻击与瞬态攻击、测信道攻击的关系针对 Meltdown 攻击的防御硬件级防御Summary什么是 Meltdown 攻击? Meltdown 攻击是一种利用处理器乱序执行(o…

游戏开发设计模式之状态模式

目录 状态模式在Unity中的具体实现案例是什么&#xff1f; 如何在游戏开发中有效地结合状态模式与享元模式以优化资源使用&#xff1f; 状态模式与其他设计模式&#xff08;如观察者模式、策略模式&#xff09;结合使用的实际例子有哪些&#xff1f; 在处理复杂状态变化时&…

Pytorch 自动微分注意点讲解

backward() backward()函数是pytorch框架实现自动微分的关键函数,一般通过loss.backward()调用,这里的loss一般是标量张量 import numpy as np import torch device torch.device(mps if torch.backends.mps.is_available() else cpu) print(device ) data1 torch.randint(…

Web3链上聚合器声呐已全球上线,开启区块链数据洞察新时代

在全球区块链技术高速发展的浪潮中&#xff0c;在创新发展理念的驱动下&#xff0c;区块链领域的工具类应用备受资本青睐。 2024年8月20日&#xff0c;由生纳&#xff08;香港&#xff09;国际集团倾力打造的一款链上应用工具——“声呐链上聚合器”&#xff0c;即“声呐链上数…

Prompt-Tuning 和 LoRA大模型微调方法区别

Prompt-Tuning 和 LoRA&#xff08;Low-Rank Adaptation&#xff09;都是在预训练语言模型基础上进行微调的方法&#xff0c;它们有以下一些区别&#xff1a; 一、调整方式 Prompt-Tuning&#xff1a; 主要是通过优化特定任务的提示&#xff08;prompt&#xff09;来实现微调。…

Eureka Server高可用模式详解:实现无缝的故障转移与容灾

目录 引言 Eureka Server背景与重要性高可用模式的必要性故障转移与容灾的核心概念 Eureka Server概述 Eureka架构简介Eureka Server与Eureka Client的工作机制Eureka在微服务架构中的角色与功能 Eureka Server的单节点架构及其局限性 单节点部署的特点单点故障的影响面临的挑…

Adobe Animate (AN)软件安装,硬件配置(附安装包)

目录 一、Adobe An 软件简介 Adobe An 软件的特点 Adobe An 软件的优势 下载 二、Adobe An 软件安装 安装前的准备工作 安装过程中的注意事项 安装后的设置 三、Adobe An 软件使用 高级动画技巧 交互设计 优化与性能提升 四、Adobe An 软件快捷键 选择工具快捷键…

设计模式 - 行为型模式(第六章)

目录 6、行为型模式 6.1 模板方法模式 6.1.1 概述 6.1.2 结构 6.1.3 案例实现 6.1.3 优缺点 6.1.4 适用场景 6.1.5 JDK源码解析 6.2 策略模式 6.2.1 概述 6.2.2 结构 6.2.3 案例实现 6.2.4 优缺点 6.2.5 使用场景 6.2.6 JDK源码解析 6.3 命令模式 6.3.1 概述 …