以下是Java 常用 API 的系统性总结,特别适用于算法竞赛(如 CCF CSP、蓝桥杯、NOI)场景。按照功能分类,并给出代表性方法及简要用法说明,方便复习与带入考场:
✅ Java 常用 API 分类总结(算法竞赛篇)
常见数据类型的数据范围与默认值
当然可以!以下是 Java 中常见数据类型的数据范围与默认值总结,适合你背诵、练习和面试使用:
✅ 一、Java 基本数据类型总结(8种)
类型 | 占用空间 | 范围(含正负) | 默认值 |
---|---|---|---|
byte |
1 字节 | -128 ~ 127 | 0 |
short |
2 字节 | -32,768 ~ 32,767 | 0 |
int |
4 字节 | -2³¹ ~ 2³¹-1(-2,147,483,648 ~ 2,147,483,647) | 0 |
long |
8 字节 | -2⁶³ ~ 2⁶³-1(约 ±9 × 10¹⁸) | 0L |
float |
4 字节 | 约 ±3.4 × 10³⁸(7位有效数字) | 0.0f |
double |
8 字节 | 约 ±1.8 × 10³⁰⁸(15位有效数字) | 0.0d |
char |
2 字节 | 0 ~ 65535(Unicode 字符) | '\u0000' |
boolean |
1 位(JVM 按 1 字节处理) | true / false |
false |
✅ 二、常用封装类与常量(用于边界判断)
类型 | 最大值 | 最小值 | 示例 |
---|---|---|---|
Integer |
Integer.MAX_VALUE = 2147483647 |
Integer.MIN_VALUE = -2147483648 |
int a = Integer.MAX_VALUE; |
Long |
Long.MAX_VALUE = 9223372036854775807L |
Long.MIN_VALUE = -9223372036854775808L |
|
Double |
Double.MAX_VALUE ≈ 1.79769e+308 |
Double.MIN_VALUE ≈ 4.9e-324(最小正数) |
注意 Double.MIN_VALUE 是正数 |
Float |
Float.MAX_VALUE ≈ 3.40282e+38 |
Float.MIN_VALUE ≈ 1.4e-45 |
同上 |
Character |
Character.MAX_VALUE = 65535 ('\uffff' ) |
Character.MIN_VALUE = 0 ('\u0000' ) |
✅ 三、如何选择数据类型?(实用建议)
场景 | 推荐类型 | 原因 |
---|---|---|
统计、计数 | int / long |
小数据用 int ,大数据(>10⁹)用 long |
精确小数运算 | BigDecimal |
如货币计算,避免精度丢失 |
浮点计算 | double |
精度高于 float |
布尔开关 | boolean |
性能优,语义清晰 |
单个字符 | char |
适合处理字符型数据 |
✅ 四、经典面试题:Integer.MIN_VALUE
的绝对值是多少?
System.out.println(Math.abs(Integer.MIN_VALUE)); // 输出仍为 -2147483648
这是因为 abs()
溢出!因为 -2147483648
没有正数对应(超出 int 正范围)。
✅ 五、背诵口诀助记法:
byte 短 short 刚好 32k,
int 四字节到 21亿。
long 八字节超千万亿,
float double 飘到天际。
char 是 Unicode,boolean 真或假。
1️⃣ 字符串处理(String
类)
方法 | 用途 | 示例 |
---|---|---|
length() |
获取字符串长度 | "abc".length() -> 3 |
charAt(i) |
获取索引 i 处字符 | "abc".charAt(1) -> 'b' |
substring(start, end) |
截取子串(左闭右开) | "abcdef".substring(1,4) -> "bcd" |
indexOf(str) |
第一次出现位置 | "ababc".indexOf("ab") -> 0 |
lastIndexOf(str) |
最后一次出现位置 | "ababc".lastIndexOf("ab") -> 2 |
contains(str) |
是否包含 | "abc".contains("b") -> true |
split(regex) |
按正则分割为数组 | "a,b,c".split(",") -> [a, b, c] |
equals(str) |
是否相等 | "abc".equals("abc") -> true |
compareTo(str) |
字典序比较 | "abc".compareTo("abd") -> -1 |
toCharArray() |
转为字符数组 | "abc" -> ['a','b','c'] |
replace(old, new) |
替换子串 | "aabb".replace("a", "c") -> "ccbb" |
trim() |
去除首尾空格 | " a b ".trim() -> "a b" |
toLowerCase() |
转小写 | "ABC".toLowerCase() -> "abc" |
toUpperCase() |
转大写 | "abc".toUpperCase() -> "ABC" |
startsWith(str) |
以某串开头 | "abc".startsWith("a") -> true |
endsWith(str) |
以某串结尾 | "abc".endsWith("c") -> true |
2️⃣ 高效字符串拼接(StringBuilder
/ StringBuffer
)
方法 | 用途 |
---|---|
append(str) |
添加字符串 |
insert(pos, str) |
插入字符串 |
delete(start, end) |
删除子串 |
reverse() |
翻转字符串 |
toString() |
转回字符串 |
⚠️
StringBuilder
非线程安全,StringBuffer
线程安全(但慢)。
3️⃣ 数学计算(Math
类)
方法 | 说明 |
---|---|
abs(x) |
绝对值 |
max(a, b) / min(a, b) |
最大 / 最小值 |
pow(a, b) |
a 的 b 次幂 |
sqrt(x) |
平方根 |
cbrt(x) |
立方根 |
ceil(x) |
向上取整 |
floor(x) |
向下取整 |
round(x) |
四舍五入(返回 long ) |
random() |
返回 [0,1) 间随机数 |
log(x) / log10(x) |
自然对数 / 常用对数 |
sin(x) , cos(x) , tan(x) |
三角函数(弧度) |
4️⃣ 数组处理(Arrays
工具类)
方法 | 说明 |
---|---|
sort(array) |
升序排序 |
binarySearch(array, key) |
二分查找(有序数组) |
fill(array, val) |
全部填充为 val |
copyOf(arr, newLen) |
拷贝前 newLen 个元素 |
equals(a, b) |
比较两个数组是否完全相等 |
toString(arr) |
数组转字符串 |
asList(T... arr) |
数组转固定长度 List |
5️⃣ 集合框架
List(ArrayList
/ LinkedList
)
方法 | 说明 |
---|---|
add(e) |
添加元素 |
remove(i) |
删除索引 i 元素 |
get(i) |
获取元素 |
set(i, e) |
修改元素 |
size() |
获取大小 |
contains(e) |
是否包含元素 |
Set(HashSet
/ TreeSet
)
方法 | 说明 |
---|---|
add(e) |
添加元素 |
remove(e) |
移除元素 |
contains(e) |
是否存在 |
size() |
元素个数 |
first() / last() |
TreeSet 中的最小 / 最大值 |
ceiling(e) |
≥e 的最小值 |
floor(e) |
≤e 的最大值 |
✅ 一、常见 Set 实现类对比
类名 | 是否有序 | 是否允许 null | 是否线程安全 | 底层结构 |
---|---|---|---|---|
HashSet |
❌ 无序 | ✅ 允许一个 | ❌ 不安全 | 哈希表 |
LinkedHashSet |
✅ 插入顺序 | ✅ 允许一个 | ❌ 不安全 | 哈希表 + 双向链表 |
TreeSet |
✅ 升序 | ❌ 不允许 | ❌ 不安全 | 红黑树(平衡二叉树) |
✅ 二、常用 Set API 汇总
功能 | 示例代码 | 说明 |
---|---|---|
初始化 | Set<Integer> set = new HashSet<>(); |
常用 HashSet |
添加元素 | set.add(x); |
添加元素(若重复则不变) |
删除元素 | set.remove(x); |
删除元素(若无也不报错) |
是否包含 | set.contains(x); |
判断元素是否存在 |
获取大小 | set.size(); |
元素个数 |
清空集合 | set.clear(); |
清空所有元素 |
是否为空 | set.isEmpty(); |
是否为空 |
遍历元素 | for (int x : set) |
常用 for-each 遍历 |
转为 List | new ArrayList<>(set); |
若需排序或索引操作 |
集合并集(新增) | set.addAll(otherSet); |
将另一个集合元素加入本集合 |
集合交集(保留) | set.retainAll(otherSet); |
只保留两集合都存在的元素 |
集合差集(去除) | set.removeAll(otherSet); |
去除在 otherSet 中也有的元素 |
✅ 三、典型代码模板
📌 1. 初始化 + 去重存储
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(2); // 无效重复
System.out.println(set); // 输出 [1, 2]
📌 2. 遍历 + 判断是否存在
for (int x : set) {
if (x == 3) System.out.println("存在");
}
📌 3. 使用 TreeSet
实现自动排序
Set<String> treeSet = new TreeSet<>();
treeSet.add("banana");
treeSet.add("apple");
System.out.println(treeSet); // [apple, banana]
✅ 四、适配题型速查
题型类别 | 用法建议 | 举例题目 |
---|---|---|
去重统计 | 用 HashSet 存入后 size() |
[CSP] 数字种类、集合大小 |
判断是否重复 | set.contains(x) + set.add(x) |
力扣 217、219 |
保持顺序去重 | 用 LinkedHashSet |
输出按插入顺序的唯一值序列 |
需要有序输出 | 用 TreeSet + 自动排序 |
力扣 349、350(交集类) |
✅ 五、背诵口诀:“增删查,去重快,TreeSet 排序不带盖!”
✅ 六、你可以练习的题目(推荐)
平台 | 题目 ID / 名称 | 涉及 API |
---|---|---|
力扣 | 217. 存在重复元素 | set.contains() |
力扣 | 349. 两个数组的交集 | set.retainAll() |
力扣 | 705. 设计哈希集合 | 手写 Set 底层结构 |
CSP | 出现过的数、不同的数种类统计 | set.add() + set.size() |
Map(HashMap
/ TreeMap
)
方法 | 说明 |
---|---|
put(key, val) |
添加键值对 |
get(key) |
获取值(无则返回 null) |
getOrDefault(key, def) |
若无该 key,返回默认值 |
containsKey(key) |
是否包含键 |
keySet() |
所有 key 集合 |
values() |
所有 value |
entrySet() |
所有 entry(用于遍历) |
频次统计(CSP常见)
map.put(x, map.getOrDefault(x, 0) + 1);
6️⃣ Collections 工具类(操作集合)
方法 | 说明 |
---|---|
sort(list) |
升序排序 |
reverse(list) |
反转列表 |
shuffle(list) |
随机打乱 |
frequency(list, obj) |
统计出现次数 |
swap(list, i, j) |
交换元素 |
rotate(list, k) |
向右旋转 k 位 |
max(list) / min(list) |
最大 / 最小值 |
7️⃣ 优先队列(PriorityQueue
)
PriorityQueue<Integer> pq = new PriorityQueue<>(); // 默认小根堆
pq.offer(3); pq.offer(1); pq.offer(2);
pq.peek(); // 1
pq.poll(); // 弹出最小的 1
自定义大根堆:
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
8️⃣ 位运算(Integer
类)
方法 | 说明 |
---|---|
bitCount(i) |
二进制中 1 的个数 |
highestOneBit(i) |
最高位的 1 的值(如 1000) |
lowestOneBit(i) |
最低位的 1 的值(如 0001) |
toBinaryString(i) |
二进制表示 |
parseInt(s, radix) |
任意进制转十进制 |
toHexString(i) |
十六进制表示 |
9️⃣ 大数处理
BigInteger
方法 | 说明 |
---|---|
add() / subtract() / multiply() / divide() |
四则运算 |
mod() |
取模 |
pow(n) |
幂 |
compareTo(BigInteger) |
比较大小 |
gcd() |
最大公约数 |
isProbablePrime(certainty) |
判断素数(概率) |
BigInteger a = new BigInteger("123456789");
BigInteger b = new BigInteger("987654321");
a.add(b).toString(); // 字符串输出
BigDecimal
方法 | 说明 |
---|---|
add() , subtract() , multiply() , divide() |
高精度小数运算 |
setScale(n, RoundingMode) |
设置小数位数 |
compareTo() |
比较大小 |
🔟 时间函数
方法 | 说明 |
---|---|
System.currentTimeMillis() |
当前毫秒时间戳 |
System.nanoTime() |
纳秒时间戳(高精度测时) |
LocalDateTime.now() |
当前时间 |
ChronoUnit.SECONDS.between(start, end) |
时间间隔 |
🔟+1 正则表达式处理
类 | 方法 |
---|---|
Pattern.compile(regex) |
编译正则 |
matcher(str) |
创建匹配器 |
find() / group() |
匹配和提取子串 |
matches() |
整体匹配是否成功 |
🔟+2 输入输出(Scanner
, BufferedReader
, PrintWriter
)
类 | 方法 |
---|---|
Scanner |
next() , nextLine() , nextInt() |
BufferedReader |
高效读取(支持 readLine() ) |
PrintWriter |
快速输出,println() 、flush() |
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
String s = br.readLine();
out.println(s);
out.flush();
常考模块
自定义排序
你提的问题非常好!作为初学者,理解 Arrays.sort()
和 Collections.sort()
的区别与适用场景,对于写出更规范、稳定、灵活的排序代码至关重要。以下是详细解释和通用解法整理👇
✅ 一、Arrays.sort()
和 Collections.sort()
有什么区别?
比较点 | Arrays.sort() |
Collections.sort() |
---|---|---|
适用对象 | 数组(如 int[] 、String[] 、Integer[] ) |
List(如 ArrayList 、LinkedList ) |
所在包 | java.util.Arrays |
java.util.Collections |
排序方式 | 默认升序;支持传入比较器 Comparator |
默认升序;也支持传入比较器 Comparator |
是否稳定排序 | 对对象数组使用 TimSort (稳定) |
使用 List.sort() ,底层同样是 TimSort (稳定) |
是否可以混用 | ❌ 不可以直接混用 | ✅ List 和 Collections.sort() 是配套的 |
🚫 不可以这样混用:
List<Integer> list = new ArrayList<>();
Arrays.sort(list); // ❌ 错误,Arrays.sort 不能用于 List
✅ 正确用法:
int[] arr = new int[]{5, 2, 3};
Arrays.sort(arr); // 用于基本类型数组
List<Integer> list = new ArrayList<>(List.of(5, 2, 3));
Collections.sort(list); // 用于 List
✅ 二、二者的典型应用对比
🧩 排序数组 → 用 Arrays.sort()
int[] arr = {4, 2, 9};
Arrays.sort(arr); // 升序:[2, 4, 9]
🧩 排序对象数组 → 仍用 Arrays.sort()
Student[] stu = ...;
Arrays.sort(stu, (a, b) -> b.score - a.score); // 按成绩降序
🧩 排序 List → 用 Collections.sort()
或 Java 8 的 list.sort()
List<String> list = new ArrayList<>(List.of("cat", "apple", "banana"));
Collections.sort(list); // 默认字典序
等价于 Java 8 写法:
list.sort(String::compareTo); // 同样是字典序
✅ 三、是否可以用 Collections.sort()
替代 Arrays.sort()
?
🟡 不能直接替代! 因为它们的参数类型不同!
Arrays.sort()
适用于 数组(如int[]
,Integer[]
,String[]
等)Collections.sort()
适用于 List(如ArrayList
,LinkedList
)
但你可以将数组转成 List 来使用 Collections.sort()
(用于对象类型数组):
✅ 数组转 List 再排序(适用于对象数组)
Integer[] arr = {5, 2, 9};
List<Integer> list = Arrays.asList(arr);
Collections.sort(list); // 排序后 arr 也变化(共享内存)
⚠️ 注意: Arrays.asList()
返回的是一个固定长度的列表,不能增删元素,只能排序或修改已有元素。
✅ 四、初学者建议记忆总结
想排序什么? | 用什么? | 支持自定义比较器? | 是否稳定? |
---|---|---|---|
int[] / double[] | Arrays.sort() |
❌ | ✅ |
Integer[] / 对象数组 | Arrays.sort(arr, cmp) |
✅ | ✅ |
List(ArrayList) | Collections.sort() / list.sort() |
✅ | ✅ |
Map | 转为 entrySet() 的 List 后排序 |
✅ | ✅ |
✨ 五、通用排序封装函数(建议记忆)
// 通用对象排序(List)
public static <T> void sortList(List<T> list, Comparator<T> cmp) {
Collections.sort(list, cmp);
}
// 通用数组排序(对象数组)
public static <T> void sortArray(T[] arr, Comparator<T> cmp) {
Arrays.sort(arr, cmp);
}
在 Java 中进行“自定义排序”,常用于对数组、对象集合(如 List
)等进行灵活的排序。CSP 考试中,常见场景包括:
- 对多个字段排序(如先按成绩,再按学号)
- 自定义结构(如 Pair)排序
- 降序 / 升序控制
✅ 一、对数组进行自定义排序(基本类型)
import java.util.*;
public class CustomSortArray {
public static void main(String[] args) {
Integer[] arr = {5, 2, 8, 1, 9};
// 降序排序
Arrays.sort(arr, (a, b) -> b - a);
System.out.println(Arrays.toString(arr)); // [9, 8, 5, 2, 1]
}
}
注意:
int[]
不能直接使用 Lambda 表达式,必须使用Integer[]
对象数组。
✅ 二、自定义结构排序(如 Pair)
import java.util.*;
class Pair {
int id, score;
public Pair(int id, int score) {
this.id = id;
this.score = score;
}
@Override
public String toString() {
return "(" + id + "," + score + ")";
}
}
public class CustomSortPair {
public static void main(String[] args) {
List<Pair> list = new ArrayList<>();
list.add(new Pair(1, 90));
list.add(new Pair(3, 80));
list.add(new Pair(2, 90));
// 排序规则:先按 score 降序,再按 id 升序
Collections.sort(list, (a, b) -> {
if (a.score != b.score) return b.score - a.score; // 降序
return a.id - b.id; // 升序
});
for (Pair p : list) {
System.out.println(p);
}
}
}
✅ 三、二维数组排序(常见于模拟/DP)
import java.util.*;
public class CustomSort2DArray {
public static void main(String[] args) {
int[][] data = {
{1, 90},
{2, 100},
{3, 90}
};
// 按第二列降序,再按第一列升序
Arrays.sort(data, (a, b) -> {
if (a[1] != b[1]) return b[1] - a[1];
return a[0] - b[0];
});
for (int[] row : data) {
System.out.println(Arrays.toString(row));
}
}
}
✅ 四、优先队列中的自定义排序(堆)
import java.util.*;
public class CustomPriorityQueue {
public static void main(String[] args) {
// 最小堆按第一维升序,第二维降序
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> {
if (a[0] != b[0]) return a[0] - b[0];
return b[1] - a[1];
});
pq.offer(new int[]{2, 100});
pq.offer(new int[]{1, 80});
pq.offer(new int[]{2, 90});
while (!pq.isEmpty()) {
System.out.println(Arrays.toString(pq.poll()));
}
}
}
📌 总结:常用模板
// Arrays.sort + Lambda
Arrays.sort(array, (a, b) -> {
// 优先级:先按某字段,再按其他字段
return a[0] - b[0]; // 升序 / b[0] - a[0] 降序
});
// List + Collections.sort
Collections.sort(list, (a, b) -> {
if (a.score != b.score) return b.score - a.score;
return a.id - b.id;
});
// PriorityQueue + 自定义比较器
PriorityQueue<Node> pq = new PriorityQueue<>((a, b) -> a.weight - b.weight);
以下是为 CCF CSP 考试 精心整理的「常见排序题速查表」,包含常考题型、解题策略、排序维度、API 示例等,帮助你快速定位解题方案。
✅ 一、速查表概览(按题型分类)
题型类别 | 排序关键 | 排序维度 | 示例题目 | 典型排序语句 |
---|---|---|---|---|
成绩/统计排序 | 值大小 | 降序或多字段 | 成绩排序、热度统计等 | Collections.sort(list, (a,b)->b.score-a.score) |
时间线排序 | 时间戳 | 升序 | 日志记录、任务调度等 | Arrays.sort(arr, Comparator.comparingInt(a -> a.time)) |
坐标排序 | x/y轴 | 多字段(先 x 后 y) | 坐标扫描、区间合并等 | Arrays.sort(points, (a,b)->a[0]==b[0]?a[1]-b[1]:a[0]-b[0]) |
编号优先 | 编号 | 按编号升序 | 按原始输入顺序输出 | Collections.sort(list, Comparator.comparingInt(a->a.id)) |
并查集/连通块 | 集合大小 | 降序或升序 | 连通块排序输出 | 自定义结构 + 统计 + 排序 |
哈希计数 | 次数 | 降序 + 值本身升序 | 频率统计、单词计数等 | 统计完后 List<Map.Entry> 排序 |
✅ 二、各类型题型详解与模板
1️⃣ 成绩类排序(权重/成绩/频次)
例题:
- 成绩排行
- 词频统计
class Student {
int id, score;
}
Collections.sort(list, (a, b) -> {
if (a.score != b.score) return b.score - a.score;
return a.id - b.id;
});
2️⃣ 时间类排序(时间戳、操作先后)
例题:
- 日志排序
- 任务调度优先队列
Arrays.sort(events, (a, b) -> a.time - b.time); // 按时间升序
或使用对象:
class Event {
int time, id;
}
Collections.sort(list, Comparator.comparingInt(e -> e.time));
3️⃣ 坐标/区间排序
例题:
- 扫描线、区间覆盖、最大重叠区间
Arrays.sort(intervals, (a, b) -> {
if (a[0] != b[0]) return a[0] - b[0]; // 起点升序
return a[1] - b[1]; // 终点升序
});
4️⃣ Map排序(基于统计结果)
例题:
- 词频统计
- 热度排序
Map<Integer, Integer> count = new HashMap<>();
for (int x : arr) count.put(x, count.getOrDefault(x, 0) + 1);
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(count.entrySet());
// 按频率降序、数值升序排序
Collections.sort(list, (a, b) -> {
if (!a.getValue().equals(b.getValue()))
return b.getValue() - a.getValue();
return a.getKey() - b.getKey();
});
5️⃣ 并查集/连通块大小排序
例题:
- 社交网络连通块分析
- 亲戚关系等题
// 每个集合统计 size[]
// 然后将 size 按 root 排序
List<int[]> groups = new ArrayList<>();
for (int i = 1; i <= n; i++) {
if (find(i) == i) groups.add(new int[]{i, size[i]});
}
groups.sort((a, b) -> b[1] - a[1]); // 按集合大小降序
6️⃣ 字典序排序(字符串或数组)
例题:
- 最小字典序拼接
- 拼接序列/排序
Arrays.sort(strings); // 默认字典序
Arrays.sort(strings, Comparator.reverseOrder()); // 字典序逆序
✅ 三、优先队列(堆)排序应用速查
需求类型 | 排序维度 | 声明语句 |
---|---|---|
最小值优先队列 | 值升序 | PriorityQueue<Integer> pq = new PriorityQueue<>(); |
最大值优先队列 | 值降序 | PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->b-a); |
多字段堆排序 | 数组 or 对象 | PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->{...}); |
✅ 四、常用 Java API 排序参考
用法 | 示例代码 |
---|---|
数组默认排序 | Arrays.sort(arr); |
对象数组排序 | Arrays.sort(objArr, (a, b) -> ...); |
List 排序 | Collections.sort(list, (a, b) -> ...); |
Map 排序(按值) | 见上面 Map 示例 |
PriorityQueue 自定义顺序 | new PriorityQueue<>((a,b) -> ...) |
📌 附:CCF CSP 排序常见题目清单(历年)
年份 | 题目名称 | 排序类型 |
---|---|---|
202206 | 通信网络 | 拓扑序 / 时间戳 |
202012 | 直播获奖名单 | 词频统计 + 排序 |
201809 | 元素选择器 | DOM序树结构排序 |
201712 | 游戏 | 结构排序 + 计分 |
201703 | 学生排队 | 优先队列 / 模拟 |
🔐 五、哈希/计数
HashMap
,HashSet
:用于快速查找TreeMap
,TreeSet
:自动排序Map.getOrDefault(k, v)
:避免空指针int[] cnt = new int[m + 1];
:模拟 HashMap 的计数数组,常用于编号有限的场景(如 CSP)
🧩 六、模拟题常用
Scanner
:快速读入StringTokenizer
(复杂输入场景)或BufferedReader
- 多维数组处理:
int[][] arr = new int[n][m];
- 模拟移动:方向数组
dx/dy
🔄 七、并查集(Union-Find)
int[] fa = new int[n];
for (int i = 0; i < n; i++) fa[i] = i;
int find(int x) { return fa[x] == x ? x : (fa[x] = find(fa[x])); }
void union(int x, int y) { fa[find(x)] = find(y); }
🌉 八、图论常用
✅ 图存储
- 邻接表:
List<List<Integer>> graph = new ArrayList<>();
- 邻接矩阵:
int[][] graph = new int[n][n];
✅ 遍历
- DFS / BFS 模板
Queue<Integer> q = new LinkedList<>();
📐 九、动态规划(DP)
- 通常使用
int[]
,int[][]
进行状态压缩或转移 - 可用
Arrays.fill()
进行初始化 - 多维数组状态转移举例:
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
💡 十、贪心算法
- 常用
Arrays.sort()
或Collections.sort()
后进行策略判断 - 自定义排序:
Arrays.sort(arr, (a, b) -> a[0] - b[0]);
- 优先队列(堆):
PriorityQueue<Integer> pq = new PriorityQueue<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
pq.offer(x); pq.poll(); pq.peek();
🧮 十一、常用工具类
功能 | API |
---|---|
比较两个对象 | Objects.equals(a, b) |
大整数运算 | BigInteger (如加减乘除、取模、幂) |
高精度小数 | BigDecimal |
Java API 使用示例代码
当然可以,下面我将针对 CSP 常考模块(字符串处理、模拟、哈希、排序、并查集、图、DP、贪心)分别提供一两个典型题目的Java API 使用示例代码,方便你快速上手实战题目。
✅ 一、字符串处理 - 示例题:“单词统计”
题目描述:给定一段英文文本,统计不同单词出现次数(不区分大小写,去除标点)。
import java.util.*;
public class WordCount {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String text = sc.nextLine().toLowerCase().replaceAll("[^a-z ]", " ");
String[] words = text.split("\\s+");
Map<String, Integer> count = new HashMap<>();
for (String word : words) {
if (!word.isEmpty()) {
count.put(word, count.getOrDefault(word, 0) + 1);
}
}
count.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
✅ 二、模拟 - 示例题:“排队打水”
题目描述:n 个人排队,每个人打水需要 t[i]
分钟,问最小总等待时间。
import java.util.*;
public class WaterQueue {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] t = new int[n];
for (int i = 0; i < n; i++) t[i] = sc.nextInt();
Arrays.sort(t); // 排序是贪心核心
long res = 0;
for (int i = 0; i < n; i++) {
res += (long) t[i] * (n - i - 1); // 模拟等待时间
}
System.out.println(res);
}
}
✅ 三、哈希 - 示例题:“出现次数统计”
题目描述:输入 n 个整数,输出每个数的出现次数。
import java.util.*;
public class FrequencyCount {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
int x = sc.nextInt();
map.put(x, map.getOrDefault(x, 0) + 1);
}
for (int key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
}
}
✅ 四、排序 - 示例题:“按得分排序”
题目描述:输入学生姓名与分数,按分数从高到低排序输出。
import java.util.*;
public class ScoreSort {
static class Student {
String name; int score;
Student(String n, int s) { name = n; score = s; }
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
List<Student> list = new ArrayList<>();
for (int i = 0; i < n; i++)
list.add(new Student(sc.next(), sc.nextInt()));
list.sort((a, b) -> b.score - a.score); // 自定义排序
for (Student s : list)
System.out.println(s.name + " " + s.score);
}
}
✅ 五、并查集 - 示例题:“朋友圈数量”
题目描述:有 n 个人,m 对朋友关系,问总共有多少个朋友圈(连通块)。
import java.util.*;
public class UnionFindFriends {
static int[] fa;
static int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
static void union(int x, int y) {
fa[find(x)] = find(y);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
fa = new int[n + 1];
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 0; i < m; i++) union(sc.nextInt(), sc.nextInt());
Set<Integer> roots = new HashSet<>();
for (int i = 1; i <= n; i++) roots.add(find(i));
System.out.println(roots.size()); // 连通块个数
}
}
✅ 六、图 - 示例题:“最短路径 BFS”
题目描述:无权图从点 A 到点 B 的最短路径长度。
import java.util.*;
public class BFSShortestPath {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
List<List<Integer>> g = new ArrayList<>();
for (int i = 0; i <= n; i++) g.add(new ArrayList<>());
for (int i = 0; i < m; i++) {
int u = sc.nextInt(), v = sc.nextInt();
g.get(u).add(v); g.get(v).add(u);
}
int[] dist = new int[n + 1];
Arrays.fill(dist, -1);
Queue<Integer> q = new LinkedList<>();
q.offer(1); dist[1] = 0;
while (!q.isEmpty()) {
int cur = q.poll();
for (int nei : g.get(cur)) {
if (dist[nei] == -1) {
dist[nei] = dist[cur] + 1;
q.offer(nei);
}
}
}
System.out.println(dist[n]);
}
}
✅ 七、动态规划(DP)- 示例题:“背包问题”
import java.util.*;
public class ZeroOneKnapsack {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), W = sc.nextInt();
int[] w = new int[n + 1], v = new int[n + 1];
for (int i = 1; i <= n; i++) {
w[i] = sc.nextInt(); v[i] = sc.nextInt();
}
int[] dp = new int[W + 1];
for (int i = 1; i <= n; i++) {
for (int j = W; j >= w[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
}
}
System.out.println(dp[W]);
}
}
✅ 八、贪心 - 示例题:“活动安排问题”
题目描述:选择最多不重叠的活动。
import java.util.*;
public class GreedyActivitySelect {
static class Act {
int start, end;
Act(int s, int e) { start = s; end = e; }
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
List<Act> list = new ArrayList<>();
for (int i = 0; i < n; i++)
list.add(new Act(sc.nextInt(), sc.nextInt()));
list.sort(Comparator.comparingInt(a -> a.end)); // 按结束时间排序
int cnt = 0, lastEnd = 0;
for (Act a : list) {
if (a.start >= lastEnd) {
cnt++; lastEnd = a.end;
}
}
System.out.println(cnt);
}
}
蓝桥杯万字攻略:算法模板大放送!-Java 版
主要内容
由数据范围反推算法复杂度以及算法内容
一般ACM或者笔试题的时间限制是1秒或2秒。在这种情况下,Java代码中的操作次数控制在 (10^7 \sim 10^8) 为最佳。
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
数据范围 | 时间复杂度 | 算法 |
---|---|---|
(n \le 30) | 指数级别 | dfs+剪枝,状态压缩dp |
(n \le 100) | (O(n^3)) | Floyd,dp,高斯消元 |
(n \le 1000) | (O(n2)),(O(n2logn)) | dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford |
(n \le 10000) | (O(n * \sqrt n)) | 块状链表、分块、莫队 |
(n \le 100000) | (O(nlogn)) | 各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树 |
(n \le 1000000) | (O(n)), 以及常数较小的 (O(nlogn)) 算法 | 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 (O(nlogn)) 的做法:sort、树状数组、heap、dijkstra、spfa |
(n \le 10000000) | (O(n)) | 双指针扫描、kmp、AC自动机、线性筛素数 |
(n \le 10^9) | (O(\sqrt n)) | 判断质数 |
(n \le 10^{18}) | (O(logn)) | 最大公约数,快速幂,数位DP |
(n \le 10^{1000}) | (O((logn)^2)) | 高精度加减乘除 |
(n \le 10^{100000}) | (O(logk \times loglogk),k表示位数) | 高精度加减、FFT/NTT 由数据范围反推算法 |
基础算法
快速排序算法模板
import java.util.Arrays;
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[low + (high - low) / 2];
int i = low - 1;
int j = high + 1;
while (true) {
while (arr[++i] < pivot);
while (arr[--j] > pivot);
if (i >= j) {
return j;
}
swap(arr, i, j);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
归并排序算法模板
import java.util.Arrays;
public class MergeSort {
private static int[] temp;
public static void mergeSort(int[] arr) {
temp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = l + (r - l) / 2;
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
private static void merge(int[] arr, int l, int mid, int r) {
System.arraycopy(arr, l, temp, l, r - l + 1);
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r) {
if (temp[i] <= temp[j]) {
arr[k++] = temp[i++];
} else {
arr[k++] = temp[j++];
}
}
while (i <= mid) {
arr[k++] = temp[i++];
}
while (j <= r) {
arr[k++] = temp[j++];
}
}
}
整数二分算法模板
public class BinarySearch {
public static int bsearch_1(int[] arr, int target) {
int l = 0, r = arr.length - 1;
while (l < r) {
int mid = l + (r - l) / 2;
if (arr[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
public static int bsearch_2(int[] arr, int target) {
int l = 0, r = arr.length - 1;
while (l < r) {
int mid = l + (r - l + 1) / 2;
if (arr[mid] <= target) {
l = mid;
} else {
r = mid - 1;
}
}
return l;
}
}
浮点数二分算法模板
public class BinarySearchFloat {
public static double bsearch_3(double l, double r) {
final double eps = 1e-6;
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) {
r = mid;
} else {
l = mid;
}
}
return l;
}
private static boolean check(double x) {
return false;
}
}
高精度加法
import java.util.ArrayList;
import java.util.List;
public class HighPrecisionAddition {
public static List<Integer> add(List<Integer> A, List<Integer> B) {
if (A.size() < B.size()) {
return add(B, A);
}
List<Integer> C = new ArrayList<>();
int t = 0;
for (int i = 0; i < A.size(); i++) {
t += A.get(i);
if (i < B.size()) {
t += B.get(i);
}
C.add(t % 10);
t /= 10;
}
if (t != 0) {
C.add(t);
}
while (C.size() > 1 && C.get(C.size() - 1) == 0) {
C.remove(C.size() - 1);
}
return C;
}
}
高精度减法
import java.util.ArrayList;
import java.util.List;
public class HighPrecisionSubtraction {
public static List<Integer> sub(List<Integer> A, List<Integer> B) {
List<Integer> C = new ArrayList<>();
int t = 0;
for (int i = 0; i < A.size(); i++) {
t = A.get(i) - t;
if (i < B.size()) {
t -= B.get(i);
}
C.add((t + 10) % 10);
if (t < 0) {
t = 1;
} else {
t = 0;
}
}
while (C.size() > 1 && C.get(C.size() - 1) == 0) {
C.remove(C.size() - 1);
}
return C;
}
}
高精度乘低精度
import java.util.ArrayList;
import java.util.List;
public class HighPrecisionMultiplication {
public static List<Integer> mul(List<Integer> A, int b) {
List<Integer> C = new ArrayList<>();
int t = 0;
for (int i = 0; i < A.size() || t != 0; i++) {
if (i < A.size()) {
t += A.get(i) * b;
}
C.add(t % 10);
t /= 10;
}
while (C.size() > 1 && C.get(C.size() - 1) == 0) {
C.remove(C.size() - 1);
}
return C;
}
}
高精度除以低精度
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HighPrecisionDivision {
public static List<Integer> div(List<Integer> A, int b, int[] r) {
List<Integer> C = new ArrayList<>();
int remainder = 0;
for (int i = A.size() - 1; i >= 0; i--) {
remainder = remainder * 10 + A.get(i);
C.add(remainder / b);
remainder %= b;
}
Collections.reverse(C);
while (C.size() > 1 && C.get(C.size() - 1) == 0) {
C.remove(C.size() - 1);
}
r[0] = remainder;
return C;
}
}
一维前缀和
import java.util.ArrayList;
import java.util.List;
public class PrefixSum1D {
public static List<Integer> getPrefixSum(List<Integer> arr) {
List<Integer> prefixSum = new ArrayList<>();
prefixSum.add(0);
for (int num : arr) {
prefixSum.add(prefixSum.get(prefixSum.size() - 1) + num);
}
return prefixSum;
}
public static int querySum(List<Integer> prefixSum, int l, int r) {
return prefixSum.get(r) - prefixSum.get(l - 1);
}
}
二维前缀和
import java.util.ArrayList;
import java.util.List;
public class PrefixSum2D {
public static List<List<Integer>> getPrefixSum(List<List<Integer>> matrix) {
int rows = matrix.size();
int cols = matrix.get(0).size();
List<List<Integer>> prefixSum = new ArrayList<>();
for (int i = 0; i <= rows; i++) {
List<Integer> row = new ArrayList<>();
for (int j = 0; j <= cols; j++) {
row.add(0);
}
prefixSum.add(row);
}
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= cols; j++) {
prefixSum.get(i).set(j, matrix.get(i - 1).get(j - 1) + prefixSum.get(i - 1).get(j) + prefixSum.get(i).get(j - 1) - prefixSum.get(i - 1).get(j - 1));
}
}
return prefixSum;
}
public static int querySum(List<List<Integer>> prefixSum, int x1, int y1, int x2, int y2) {
return prefixSum.get(x2).get(y2) - prefixSum.get(x1 - 1).get(y2) - prefixSum.get(x2).get(y1 - 1) + prefixSum.get(x1 - 1).get(y1 - 1);
}
}
一维差分
import java.util.ArrayList;
import java.util.List;
public class Difference1D {
public static List<Integer> getDifference(List<Integer> arr) {
List<Integer> diff = new ArrayList<>();
diff.add(arr.get(0));
for (int i = 1; i < arr.size(); i++) {
diff.add(arr.get(i) - arr.get(i - 1));
}
return diff;
}
public static void applyDifference(List<Integer> arr, int l, int r, int c) {
arr.set(l, arr.get(l) + c);
if (r + 1 < arr.size()) {
arr.set(r + 1, arr.get(r + 1) - c);
}
}
}
二维差分
import java.util.ArrayList;
import java.util.List;
public class Difference2D {
public static List<List<Integer>> getDifference(List<List<Integer>> matrix) {
int rows = matrix.size();
int cols = matrix.get(0).size();
List<List<Integer>> diff = new ArrayList<>();
for (int i = 0; i < rows; i++) {
List<Integer> row = new ArrayList<>();
row.add(matrix.get(i).get(0));
for (int j = 1; j < cols; j++) {
row.add(matrix.get(i).get(j) - matrix.get(i).get(j - 1));
}
diff.add(row);
}
return diff;
}
public static void applyDifference(List<List<Integer>> diff, int x1, int y1, int x2, int y2, int c) {
diff.get(x1).set(y1, diff.get(x1).get(y1) + c);
diff.get(x1).set(y2 + 1, diff.get(x1).get(y2 + 1) - c);
diff.get(x2 + 1).set(y1, diff.get(x2 + 1).get(y1) - c);
diff.get(x2 + 1).set(y2 + 1, diff.get(x2 + 1).get(y2 + 1) + c);
}
}
位运算
public class BitOperations {
public static int getKthBit(int n, int k) {
return (n >> k) & 1;
}
public static int lowbit(int n) {
return n & -n;
}
}
双指针算法
import java.util.ArrayList;
import java.util.List;
public class TwoPointers {
public static List<Integer> twoSum(List<Integer> nums, int target) {
int i = 0, j = nums.size() - 1;
while (i < j) {
int sum = nums.get(i) + nums.get(j);
if (sum < target) {
i++;
} else if (sum > target) {
j--;
} else {
return List.of(i, j);
}
}
return List.of();
}
}
离散化
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Discretization {
public static List<Integer> discretize(List<Integer> arr) {
List<Integer> sortedArr = new ArrayList<>(arr);
Collections.sort(sortedArr);
sortedArr = new ArrayList<>(sortedArr.stream().distinct().toList());
List<Integer> res = new ArrayList<>();
for (int num : arr) {
int idx = Collections.binarySearch(sortedArr, num);
res.add(idx + 1);
}
return res;
}
}
区间合并
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class IntervalMerge {
public static List<int[]> merge(List<int[]> intervals) {
List<int[]> res = new ArrayList<>();
if (intervals.isEmpty()) {
return res;
}
intervals.sort(Comparator.comparingInt(a -> a[0]));
int start = intervals.get(0)[0];
int end = intervals.get(0)[1];
for (int i = 1; i < intervals.size(); i++) {
int[] interval = intervals.get(i);
if (end < interval[0]) {
res.add(new int[]{start, end});
start = interval[0];
end = interval[1];
} else {
end = Math.max(end, interval[1]);
}
}
res.add(new int[]{start, end});
return res;
}
}