【刷题日记】Java 刷题的快速热身,3 分钟重拾战斗热情
由于各种原因,我在平时写代码或刷题时,每隔一定时期的就要进行语言切换(C、C++、Python、JavaScript、Java),导致一些常用的语法或写题技巧老是记混,每次卡壳都要去搜一搜。
本文将随着刷题进度持续记录一些使用 Java 语言刷题时必须记住的要素,快速解决在语法方面的障碍。如果你打算开始使用 Java 语言进行刷题时,可以通篇浏览实现快速热身。
本文主要内容:
- 必背写法与重要函数用法
- 集合增删改查与字符串操作速查
- ACM 模式
- 语言无关的通用技巧
本文题目难度标识:🟩简单,🟨中等,🟥困难。
基础语法要点
看看爪哇代码长啥样先,试图唤起尘封的记忆 😅😅😅
1 | public class Solution{ // 一个文件一个类 |
一些注意点,容易和别的语言混淆:
右移操作 | 取模运算 |
---|---|
算数右移:>> 逻辑右移: >>> |
由于 Java 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正:int modulus = (sum % k + k) % k; 相关题目:🟨 974. 和可被 K 整除的子数组 - 力扣(LeetCode) |
数组 Arrays
声明与初始化
1 | // 一维数组 |
排序 Arrays.sort
1 | int[] nums = new int[]{4,5,2,7,3,1,0,6,8,9}; |
流 Arrays.stream
Arrays.stream()
可以将数组转换为流,以使用更多的操作。
1 | int[] arr1 = {1, 2, 3, 4, 5}; |
其他
使用 Arrays.equals(array1, array2);
比较两个数组的元素是否相等。
1 | // 比较两个数组是否相等 |
Arrays.copyOf()
方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。Arrays.copyOf()
的第二个参数指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。
1 | int[] arr1 = {1, 2, 3, 4, 5}; |
Arrays.fill()
将指定的 int 值分配给指定 int 型数组的每个元素。
1 | int[] arr1 = {1, 2, 3, 4, 5}; |
字符串 String
1 | // 字符串相等判断 |
操作 | String |
StringBuffer /StringBuilder |
---|---|---|
增 | + 操作符。注意从左到右结合性 |
sb.append(str); sb.insert(int offset, str); |
删 | 不可变类 | sb.delete(start, end) sb.deleteCharAt() |
改(替换字符) | replace(char oldChar, char newChar) :返回新的字符串,原字符串不改变。replaceAll(String regex, String replacement) :返回新的字符串,原字符串不改变。replaceFirst(String regex, String replacement) :返回新的字符串,原字符串不改变。 |
sb.replace(start,end, str); :将起始位置为 start,结束位置为 end-1 的子串替换为 str。不生成新的 StringBuilder 对象,在原来的 StringBuilder 对象上修改。sb.setCharAt(index,c) |
查 | str.charAt(index); |
相同 |
长度 | str.length(); |
相同 |
StringBuilder
还提供以下函数:
sb.reverse()
:反转字符串sb.toString()
:返回字符串sb.capacity()
:返回当前容量。注意,和返回有效长度sb.length()
不一样!
字符、字符串与数字之间的转换
String | 进制型 String | Number 类 | char | char[] | |
---|---|---|---|---|---|
String | - | 静态方法:Double.parseDouble(s); Double.valueOf(s).doubleValue(); 实例方法: new Double(s).doubleValue(); |
s.toCharArray(); |
||
进制型 String | - | Integer.parseUnsignedInt(str,2); |
|||
Number 类 | ""+d; Double.toString(d); new Double(d).toString(); String.valueOf(d); |
Integer.toBinaryString(int i); Integer.toOctalString(int i); Integer.toHexString(int i); |
Long 转 int :l.intValue(); |
||
char | String.valueOf(c); |
- | |||
char[] | new String(charArr); |
- |
一招静态方法 valueOf
可以解决大部分转换难题。被转的放里面。
我们在处理输入输出字符串题目时的连招:
1 | // 把字符串转换成字符数组处理,更丝滑。不然只能疯狂 s.charAt(i) 咯 |
StringBuffer
和 StringBuilder
中的构造方法和其他方法几乎是完全一样的。StringBuilder
非线程安全,但速度更快。一般情况下(比如刷题)推荐使用 StringBuilder
。
String
保留小数精度的处理可看后续的「ACM 模式下的输入输出」章节。
基础函数
1 | // 输出 |
集合 Collection
/Map
集合增删改查速览:
接口 | Collection |
List |
Deque |
---|---|---|---|
增 | add(E e) |
add(int index,E element) |
addFirst() addLast() |
删 | remove(Object o) 移除指定对象clear() 清空所有内容 |
对空链表操作抛异常:removeFirst() removeLast() |
|
改 | set(int index, E element) |
||
查 | get(int index) |
getFirst() getLast() |
|
判断 | contains(Object o) isEmpty() |
接口 | Map |
---|---|
增 | put(key,value) |
删 | remove(key) |
改 | put(key,newValue) |
查 | get(key) getOrDefault(key,defaultValue) |
判断 | containsKey(key) containsValue(value) |
集合 | entrySet() keySet() valueSet() |
易错点:getOrDefault
的使用
getOrDefault
的使用有时候我们会编写这样的代码:map.getOrDefault(key,func());
,我们会误以为只有 map
不存在 key
时才会调用 func()
。事实上,Java 在传递参数给方法前,都会计算参数的值。因此即使 key 存在,func()
还是会执行一次。
如果我们需要 map
不存在时才执行 func()
,可以使用 computeIfAbsent
函数:map.computeIfAbsent(key,e->func());
或者使用三元运算符:int result = map.containsKey(key)? map.get(key) : func();
ACM 模式下的输入输出
相信我们已经非常熟悉 LeetCode 那种「核心代码」的写题模式,在这种模式下,我们可以更专注于代码逻辑本身。然而一些公司或学校的机考仍然使用 ACM 文件输入输出模式,在这种模式下需要对题目输入输出的逻辑进行额外的处理。
最最基本的输出写法。
1 | System.out.println("Hello Nowcoder!"); // 换行输出 IDEA 下快捷输入:sout |
java.util.Scanner
是 Java5 的新特征,我们可以通过 Scanner
类来获取用户的输入。
1 | // 创建 Scanner 对象的基本语法 |
输入时用到类 Scanner
类,里面有几个常用方法:
hasNext()
:用于判断是否还有输入的数据,常放在 while 循环中判断输入是否结束。next()
:读到空格就停止读取。适合读取单个字符或字符串。返回类型String
。nextLine()
:读取一整行数据,碰到换行则停止。返回类型String
next()
与 nextLine()
区别:
函数 | next() |
nextLine() |
---|---|---|
是否阻塞 | 一定要读取到有效字符后才可以结束输入 | |
结束符 | 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。 | 以 Enter 为结束符,该返回的是输入回车之前的所有字符。 |
带空格字符串 | 🟥 | 🟩 |
如果要输入 int
或 float
类型的数据,在 Scanner
类中也有支持,但是在输入之前最好先使用 hasNextXxx()
方法进行验证,再使用 nextXxx()
来读取。
hasNextInt()
/nextInt()
:遇到空格时会停止读取,返回的结果为空格前读取到的部分。返回类型int
。hasNextDouble()
/nextDouble()
next()
和 nextLine()
连用时的注意事项
next()
和 nextLine()
连用时的注意事项next()
、nextInt()
读取数据后指针还在当前行,如果紧跟 nextLine()
,读取数据会出错。处理方法是:增多一个 nextLine()
「吃掉」换行符即可。具体案例详看下文案例「单组字符串输入输出」。
如果全程只使用 nextInt()
不会有上述问题。
形式 A:单组/多组 + EOF/零尾模式
输入结束模式:
- EOF 结束模式:输入有多组,文件末尾即输入结束。
- 零尾模式:输入有多组,输入参数均为 0 时即输入结束。
题目:🟩 多组 A+B EOF形式 🟩 多组 A+B 零尾模式
给定两个整数 a 和 b ,请你求出 a+b 的值。
1 | ************************* EOF模式 *************************** |
1 | import java.util.Scanner; |
形式 B:T 组
给定两个整数 a 和 b ,请你求出 a+b 的值。
首先会给出一个数字 T,然后给出 T 组数字。
1 | 输入: |
1 | public static void main(String[] args) { |
形式 C:单组字符串的输出与输出
这里将探讨 nextInt()
和 nextLine()
混用的问题。
给定一个长度为 n 的字符串 s ,请你将其倒置,然后输出。
1 | 输入: |
1 | public static void main(String[] args) { |
注意到,上面的参考答案都统一使用了 nextLine()
函数读取输入。那么我们能不能用 nextInt()
与 nextLine()
混合读取呢?答案是可以的,但是容易出错。
1 | public static void main(String[] args) { |
我们发现 str 并没有读取成功,这是因为第一次在读取 nextInt()
时,指针还在当前行,第二个 nextLine()
只读取到一个换行符。解决方法是在读取 nextInt()
后,在上面代码中「位置 A」处使用 nextLine()
「吃掉」第一行的换行符即可。
形式 D:字符串格式化
给定一个小数 n ,请你保留 3 位小数后输出。
如果原来的小数位数少于 3 ,需要补充 0 。
如果原来的小数位数多于 3 ,需要四舍五入到 3 位。
看到这题目时我还纳闷,以前在 LeetCode 刷题就好少这种返回指定精度数组并要求保留小数点后的 0 的,其实仔细想想以前刷题的核心代码模式要么返回 int
或 double
,不需要我们处理输出的位数或补充 0。而这 ACM 模式输出的是 String
,对输出自然会有另外的格式化要求。
1 | 输入: |
1 | public static void main(String[] args) { |
String.format
作为文本处理工具,为我们提供强大而丰富的字符串格式化功能。
对浮点数进行格式化:占位符格式为 %[index$][标识]*[最小宽度][.精度]转换符
。
可用标识 | 可用转换符 |
---|---|
- - ,在最小宽度内左对齐,不可以与 0 标识一起使用。- 0 ,若内容长度不足最小宽度,则在左边用 0 来填充。- # ,对 8 进制和 16 进制,8 进制前添加一个 0 ,16 进制前添加 0x 。- + ,结果总包含一个 + 或 - 号。- 空格 ,正数前加空格,负数前加 - 号。- , ,只用与十进制,每 3 位数字间用 , 分隔。- ( ,若结果为负数,则用括号括住,且不显示符号。 |
- b ,布尔类型,只要实参为非 false 的布尔类型,均格式化为字符串 true ,否则为字符串 false 。- n ,平台独立的换行符, 也可通过 System.getProperty(“line.separator”) 获取。- f ,浮点数型(十进制)。显示 9 位有效数字,且会进行四舍五入。如 99.99 。- a ,浮点数型(十六进制)。- e ,指数类型。如 9.38e+5 。- g ,浮点数型(比 %f ,%a 长度短些,显示 6 位有效数字,且会进行四舍五入) |
更多示例:
String.format("%09d",a)
:正整数不足 9 位的补充前导零。(🟩 单组_补充前导零_牛客题霸_牛客网 (nowcoder.com))
语言无关的通用技巧
1 | // 循环增减 |
有时候有些对输入顺序有要求的,我们可以稍微调整一下顺序。
1 | // 函数参数交换 |
写题时常见的初始化最大最小值:
- 最大值:
0x3f3f3f3f
- 最小值:
0xc0c0c0c0
为什么 ACM 中经常使用 0x3f3f3f3f
作为整型的最大值?
0x3f3f3f3f
作为整型的最大值?原因如下:
0x3f3f3f3f
的十进制是1,061,109,567
,是10^9
级别的,而一般场合下的数据都是小于10^9
的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。而平时用的Integer.MAX_VALUE=0x7fffffff
不能满足“无穷大加一个有穷的数依然是无穷大”这个条件,加上一个数后,根据补码规范它会变成了一个很小的负数。0x3f3f3f3f
的 2 倍还是一个「无穷大」的数。0x3f3f3f3f
的每 8 位(每个字节)都是相同的。我们在程序设计中经常需要使用memset(a, val, sizeof(a))
初始化一个数组a
,该语句把数值val
(0x00
~0xFF
)填充到数组a
的每个字节上,所以用memset
只能赋值出“每 8 位都相同”的int
。当需要把一个数组中的数值初始化成正无穷时,为了避免加法算术上溢出或者繁琐的判断,我们经常用memset(a, 0x3f, sizeof(a))
给数组赋0x3f3f3f3f
的值来代替。
本文参考
- 我久远积灰的笔记
- 0x3f3f3f3f是什么意思???-CSDN博客
- 牛客网在线编程_算法笔试_输入输出练习 (nowcoder.com)
- java中的next()方法,nextline()方法,hasnext()方法的用法系列(1)。_java 读取a b c用next-CSDN博客
- Java Scanner 类 | 菜鸟教程 (runoob.com)
- java中String和StringBuilder的replace方法_stringbuilder replace-CSDN博客
- Java-修改 String 指定位置的字符最全方法总结(StringBuilder 和 StringBuffer 的使用以及区别)_string替换指定位置字符-CSDN博客
- Java保留两位小数的几种写法总结[通俗易懂]-腾讯云开发者社区-腾讯云 (tencent.com)