L1阶段题解方法总结
1、next() 和 nextLine() 的区别
nextLine()方法返回的是Enter键之前的所有字符,它是可以得到带空格的字符串的。
next()会自动消去有效字符前的空格,只返回输入的字符,不能得到带空格的字符串。
2、不同输入数据,有不同构造数据的方式
明确数量的数据可以用N个nextInt()
不确定数量的数据可以用next()或者nextLine()。如果需要.split(" ")要记得用nextLine()
3、遇到运行超时问题(非数据结构问题)
使用
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
代替
Scanner scanner = new Scanner(System.in);使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write();
代替
System.out.println();
以下的方法都是 BufferedReader 的方法
- read()
功能:read() 方法用于读取单个字符。
返回值:它返回读取的字符的整数表示(0到65535之间的值);如果已达到流的末尾,则返回 -1。
用途:这个方法通常用于需要逐字符处理输入数据的场景。
- readLine()
功能:readLine() 方法用于读取一个文本行,直到遇到换行符('\n')、回车符('\r')或回车后直接跟着换行符("\r\n")为止。
返回值:它返回一个包含该行内容的字符串,不包括任何行终止字符;如果已达到流的末尾,则返回 null。
用途:这个方法适用于按行读取文本数据的场景,特别是处理输入数据分布在多行时非常有用。
- 二者的区别
读取单位不同:read() 每次读取一个字符,而 readLine() 读取一整行文本直到遇到换行符。
返回类型不同:read() 返回的是单个字符的整数值,readLine() 返回的是一个字符串。
处理行终止符的方式不同:read() 读取的数据中包含行终止符(如果它们是输入数据的一部分),而 readLine() 在返回行内容时会自动去除行终止符。
用途和应用场景不同:read() 更适合对输入数据进行逐字符分析或处理的场景;readLine() 更适合需要按行处理输入数据的场景,例如读取配置文件、解析逐行命令等。
4、遇到运行时间超时,更换Scanner无法解决的情况
当结果运行超时问题不是更换输入方式所导致的时候,那就得考虑是否为数据结构导致的!
以下的情况是数组和HashMap的区别
检查数据结构是否可以更换为数组。数组的访问时间复杂度为O(1)
BufferedReader bu = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(bu.readLine());
int[] tag = new int[1001];for (int i = 0; i < n; i++) {String[] k = bu.readLine().split(" ");for (int j = 1; j < k.length; j++) {int temp = Integer.parseInt(k[j]);tag[temp]++;}
}4
3 889 233 2
5 100 3 233 2 73
4 3 73 889 2
2 233 123该代码所读取的数据是以上的情况。
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
Map<Integer,Integer> tagFrequency = new HashMap<>();
for (int i = 0; i < N; i++) {int K = scanner.nextInt(); // 博文的特性标签数量for (int j = 0; j < K; j++) {int tag = scanner.nextInt();tagFrequency.put(tag, tagFrequency.getOrDefault(tag, 0) + 1);}
}
这是HashMap的情况。
5、如果输入可能包含多个连续空格或在开头/结尾有空格
1. 使用split(" ")的方法:○ 这种方法将字符串按照空格分割成一个字符串数组。如果输入中有多个连续的空格,split方法会将它们视为分隔符之间的空字符串。○ 当输入恰好包含一个空格时,这种方法是有效的。但如果输入中包含多个连续的空格或者在开头/结尾有空格,这种方法可能导致数组包含空字符串或多于两个的元素。○ 示例:对于输入 "123 456"(假设中间有多个空格),split(" ")将返回一个包含多个元素的数组,其中只有第一个和最后一个元素是有意义的数字字符串。2. 使用substring和indexOf(' ')的方法:○ 这种方法直接寻找第一个空格的位置,并据此将字符串分割成两部分。它不会被连续的空格所影响。○ 这种方法在输入中恰好有一个空格时效果最佳。如果有多个连续空格,它只会识别第一个空格,因此不会受到额外空格的影响。○ 示例:对于同样的输入 "123 456",使用substring和indexOf(' ')将只会识别第一个空格,并正确地将字符串分割为 "123" 和 "456"。
总的来说,使用substring和indexOf(' ')的方法更加可靠,因为它只关注第一个空格并据此分割字符串。
相比之下,split(" ")可能会因为额外的空格而产生非预期的行为,尤其是当输入格式不严格时。
相关题目:
L1-025 正整数A+B
.......
为什么使用split不行,是因为题目保证至少存在一个空格。那么就有可能输入样例是123 456
那么如果该题目是这样去构造的话
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));String[] s = in.readLine().split(" ");String r1 = s[0];String r2 = s[1];
-----------则会存在以下的该情况-----------
[123, , , , , , , , 456]
123 + ? = ?所以得要用substring和indexOf解决
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));String s = in.readLine();String r1 = s.substring(0, s.indexOf(' '));String r2 = s.substring(s.indexOf(' ') + 1);
6、什么时候需要用到scanner.nextLine()
在Java中,使用scanner.nextLine();来读取换行符通常是在处理用户输入时,尤其是在连续读取多种类型的输入(如整数、字符串等)时非常重要。这种情况通常出现在以下几种情景:
- 在读取整数或其他数据类型后读取字符串:当你使用例如scanner.nextInt();或scanner.nextDouble();这样的方法读取非字符串类型的输入后,这些方法不会读取行尾的换行符(\n)。如果紧接着使用scanner.nextLine();来读取字符串,它会读取到之前输入后留下的换行符,并返回一个空字符串。为了避免这个问题,通常在读取完非字符串类型的数据后,先使用一次scanner.nextLine();来消耗掉这个换行符。
- 在读取多行数据时:如果你需要读取多行数据,每行都用换行符分隔,那么使用scanner.nextLine();是合适的。它会读取直到下一个换行符之前的所有字符,包括空格,这对于处理多行字符串输入非常有用。
例如,考虑以下输入场景:
int number = scanner.nextInt(); // 假设用户输入了一个数字后按下了回车
scanner.nextLine(); // 这一行读取了上一个输入后的换行符
String text = scanner.nextLine(); // 现在这一行才是真正读取用户输入的字符串
如果在这个例子中没有使用scanner.nextLine();来读取第一个数字后的换行符,
那么String text = scanner.nextLine();
这行将会立即读取到那个换行符,并返回一个空字符串,而不是预期的用户输入。
相关题目:
L1-032 Left-pad
L1-043 阅览室.......
输入的样例是
15 _
I love GPLT如果读取目标长度和填充字符之后,没有进行读取换行符的话则会出错
7、当遇到以什么输入为结束时,可以用Scanner.hasNextLine()
Scanner scanner = new Scanner(System.in);
ArrayList<String> likes = new ArrayList<>();while (scanner.hasNextLine()) {String name = scanner.nextLine();if (name.equals(".")) {break;}likes.add(name);
}
相关题目:
L1-035 情人节
8、字符矩阵旋转
这类题目的关键在于理解二维数组的索引操作和旋转后每个元素的新位置。这类问题通常涉及到数组的行列交换、反转或者对称性操作。以下是一些处理这类问题的技巧:理解旋转规则:
90度旋转:对于矩阵中的元素 matrix[i][j],在顺时针旋转90度后,它将移动到 matrix[j][n-1-i] 的位置(其中 n 是矩阵的大小)。
180度旋转:元素 matrix[i][j] 会移动到 matrix[n-1-i][n-1-j]。
270度旋转:元素 matrix[i][j] 会移动到 matrix[n-1-j][i]。
使用辅助数组:当旋转矩阵时,最简单的方法是使用一个辅助数组来存储旋转后的结果,然后再将这个结果复制回原数组。这样可以避免在原地修改时出现的复杂情况。原地旋转:对于要求原地旋转的问题,你需要小心地交换元素的位置。通常这会涉及到多步交换过程,需要仔细设计以避免覆盖某些元素。分层处理:对于正方形矩阵,可以将矩阵看作由若干层环形结构组成,从外层向内层逐层旋转。每一层可以单独处理。矩阵对称性:
对于一些特殊的旋转(如180度),可以利用矩阵的对称性。例如,180度旋转本质上就是关于中心的对称。
编码时的注意点:注意边界条件,例如索引是否会越界。在原地旋转时,注意不要过早地覆盖你还需要读取的值。可视化:在处理这类问题时,画图帮助理解元素如何移动是非常有用的。调试和测试:使用小的矩阵进行测试,逐步检查每一步的旋转是否正确。
相关题目:
L1-048 矩阵A乘以B
9、JDK8 Stream 和 Arrays.sort() 对比
StreamAPI 去重与排序
去重(Distinct):
StreamAPI中的.distinct()方法在内部使用HashSet来去除重复元素。
它适用于Stream操作,与其他Stream操作(如映射、过滤、排序等)无缝集成。
由于它是基于HashSet实现的,因此去重的效率与直接使用HashSet相似。
排序(Sorted):
Stream的.sorted()方法对元素进行排序。默认情况下,它使用自然排序(如果元素类实现了Comparable接口),也可以接受一个自定义的比较器。
它对整个Stream的元素进行排序,适用于链式操作。
排序操作在内部实现可能与Arrays.sort不同,因为它需要适应Stream的其他操作。
Arrays.sort 排序
排序:
Arrays.sort使用的是经过优化的快速排序算法(TimSort),它对数组进行原地排序。
适用于数组和可以转换为数组的集合。
它通常比Stream的排序快,尤其是在处理大型数组时。
HashSet 去重
去重:
HashSet在Java集合框架中提供了基于散列的去重功能。
直接对元素集合进行去重,不涉及其他操作(如排序、过滤等)。
HashSet在去重时非常高效,特别是当元素数量较大时。
优势与选择
StreamAPI:
优势:链式调用,可以与其他Stream操作(如映射、过滤)结合使用,代码更清晰、更易于维护。
适用场景:需要进行一系列数据处理操作(如过滤、映射、排序、去重)时,特别是在处理集合和数组以外的数据源时。
Arrays.sort:
优势:对于数组排序非常高效,尤其是在处理大型数组时。
适用场景:主要适用于需要快速、高效地对数组进行排序的情况,尤其是在性能至关重要的场景。
HashSet:
优势:在去重方面非常高效,特别是对于大型数据集。由于HashSet是基于哈希表的,因此它的插入和查询操作通常是常数时间的。
适用场景:当需要快速去除重复元素,且不关心元素的顺序时。综合比较性能:
HashSet在去重方面通常比StreamAPI更快,因为它专门为此设计。
Arrays.sort在排序方面通常比StreamAPI更快,尤其是在处理大型数组时。
使用便利性:
StreamAPI提供了更高级的抽象,使得编写链式操作更为简洁和直观。
场景适用性:
如果你的任务仅涉及排序或去重,使用Arrays.sort或HashSet可能更直接有效。
如果你的任务涉及到多个数据处理步骤(如过滤、转换、排序和去重),那么使用StreamAPI会使代码更加清晰和易于维护。
综上所述,选择哪种方法取决于你的具体需求:是否需要其他类型的数据处理、你处理的数据大小、以及对性能的敏感度。
10、正则表达式
示例 1:替换数字
java
Copy code
String text = "我有5个苹果和3个橙子。";
text = text.replaceAll("\\d", "#");
System.out.println(text); // 输出: 我有#个苹果和#个橙子。
这个例子中,\\d 是一个正则表达式,用于匹配任何数字。每个匹配到的数字都被替换成了 #。示例 2:移除非字母字符
java
Copy code
String sentence = "Hello, World! 123";
sentence = sentence.replaceAll("[^a-zA-Z]", "");
System.out.println(sentence); // 输出: HelloWorld
这里,[^a-zA-Z] 匹配任何非字母的字符,并将其移除。示例 3:替换空格
java
Copy code
String str = "这是 一个 测试。";
str = str.replaceAll("\\s+", " ");
System.out.println(str); // 输出: 这是 一个 测试。
\\s+ 匹配一个或多个空格字符,然后将其替换为单个空格。示例 4:格式化日期
java
Copy code
String dateStr = "今天是2024-01-20。";
dateStr = dateStr.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1");
System.out.println(dateStr); // 输出: 今天是20/01/2024。
这个例子中,(\\d{4})-(\\d{2})-(\\d{2}) 匹配格式为 年-月-日 的日期,然后用 $3/$2/$1 进行格式转换,使其变成 日/月/年 的格式。示例 5:替换特定单词
java
Copy code
String text = "Java是一种编程语言,非常适合于面向对象的编程。";
text = text.replaceAll("\\bJava\\b", "Python");
System.out.println(text); // 输出: Python是一种编程语言,非常适合于面向对象的编程。
这里,\\bJava\\b 匹配单词 "Java",并将其替换为 "Python"。
相关题目:
L1-064 估值一亿的AI核心代码
11、StringTokenizer的用法
Java StringTokenizer 属于 java.util 包,用于分隔字符串。StringTokenizer 构造方法:
1. StringTokenizer(String str) :构造一个用来解析 str 的 StringTokenizer 对象。java 默认的分隔符是空格("")、制表符(\t)、换行符(\n)、回车符(\r)。
2. StringTokenizer(String str, String delim) :构造一个用来解析 str 的 StringTokenizer 对象,并提供一个指定的分隔符。
3. StringTokenizer(String str, String delim, boolean returnDelims) :构造一个用来解析 str 的 StringTokenizer 对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。StringTokenizer 常用方法:
1. int countTokens():返回nextToken方法被调用的次数。
2. boolean hasMoreTokens():返回是否还有分隔符。
3. boolean hasMoreElements():判断枚举 (Enumeration) 对象中是否还有数据。
4. String nextToken():返回从当前位置到下一个分隔符的字符串。
5. Object nextElement():返回枚举 (Enumeration) 对象的下一个元素。
6. String nextToken(String delim):与 4 类似,以指定的分隔符返回结果