目录
题目链接:
题目:

解题思路:
就是一些Java的Api的使用,刚开始使用nextLine进行输入,然后使用split(" ")分割并封装到字符串数组中,这样便于后续的操作,
然后进行操作,使用contais判断是否包含某个操作进而进行操作
代码:
java
/*out
*Stack Integer ArrayList String StringBuffer peek
*Collections imports LinkedList offer return
*empty polls offerLast pollFirst isEmpty
*List Deque append length HashMap
*return remove boolean continue charAt
*toString static System println nextInt
*Scanner System toCharArray
*/
import java.util.*;
public class Main {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
int t=1;
while(t--!=0){
slove();
}
sc.close();
}
static int[] unit={59,59,23,31,12};
static int[] mon={0,31,28,31,30,31,30,31,31,30,31,30,31};
public static void slove(){
String[] s=sc.nextLine().split(" ");
//这个二维动态数组的方式要知道
List<Integer>[] a=new ArrayList[5];
for(int i=0;i<s.length;i++){
a[i] = new ArrayList<>();
if(s[i].contains("*")){
for(int j=i==4||i==3?1:0;j<=unit[i];j++)
a[i].add(j);
}else if(s[i].contains(",")){
String[] fu=s[i].split(",");
for(int j=0;j<fu.length;j++){
a[i].add(Integer.parseInt(fu[j]));
}
}else if(s[i].contains("-")){
String[] fu=s[i].split("-");
for(int j=Integer.parseInt(fu[0]);j<=Integer.parseInt(fu[1]);j++){
a[i].add(j);
}
}else{
a[i].add(Integer.parseInt(s[i]));
}
}
int ans=0;
for(int i=0;i<a[4].size();i++){
for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[a[4].get(i)];j++){
ans += a[0].size() * a[1].size() * a[2].size();
}
}
System.out.println(ans);
}
}
问题:
总结:
要理解这段代码,我们需要结合题目背景(SimpleCron 表达式解析)和代码逻辑(时间域解析、执行次数计算),从项目结构、核心变量、流程逻辑、细节实现四个维度展开分析,同时解释代码中涉及的 Java 语法和算法思想。
一、题目背景回顾:SimpleCron 表达式的规则
题目中定义的SimpleCron表达式是一个由4 个空格分隔的 5 个域组成的字符串,格式为X X X X X,每个域的含义和取值范围如下:
域的顺序 含义 取值范围 支持的特殊字符
第 1 个域 秒 0-59 *(所有值)、,(枚举)、-(范围)
第 2 个域 分钟 0-59 同上
第 3 个域 小时 0-23 同上
第 4 个域 日期 1-31 同上
第 5 个域 月份 1-12 同上
任务是计算 2023 年中该表达式对应的定时任务的总执行次数(2023 年是平年,2 月有 28 天)。
二、代码整体结构与核心变量说明
这段代码是一个标准的 Java 控制台程序,核心结构分为:
静态变量:存储全局通用数据;
main 方法:程序入口,控制执行流程;
slove 方法:核心逻辑(解析表达式、计算执行次数)。
- 静态变量解析
java
运行
static Scanner sc = new Scanner(System.in);
static int[] unit={59,59,23,31,12};
static int[] mon={0,31,28,31,30,31,30,31,31,30,31,30,31};
sc:Scanner对象,用于读取控制台输入的 SimpleCron 字符串;
unit数组:存储 5 个域的最大取值,对应关系为:
unit[0] = 59 → 秒的最大值(0-59);
unit[1] = 59 → 分钟的最大值(0-59);
unit[2] = 23 → 小时的最大值(0-23);
unit[3] = 31 → 日期的最大值(1-31);
unit[4] = 12 → 月份的最大值(1-12);
mon数组:存储 2023 年每个月的天数,mon[月份] = 该月天数(例如mon[2]=28表示 2 月有 28 天),注意mon[0]是占位符(因为月份从 1 开始)。
- main 方法:程序入口
java
运行
public static void main(String[] args) {
int t=1;
while(t--!=0){
slove();
}
sc.close();
}
这里用while(t--!=0)循环执行slove()方法,实际t=1,所以只执行 1 次;
最后调用sc.close()关闭 Scanner,避免资源泄漏。
三、核心逻辑:slove 方法的详细解析
slove方法是代码的核心,分为两步:
解析 SimpleCron 表达式:将 5 个域的取值转换为List<Integer>集合;
计算 2023 年的总执行次数:遍历合法的月份和日期,结合时分秒的组合数统计次数。
步骤 1:解析 SimpleCron 表达式(将字符串转换为 List 集合)
首先读取输入的表达式并分割为 5 个域:
java
运行
String[] s=sc.nextLine().split(" ");
List<Integer>[] a=new ArrayList[5];
s = sc.nextLine().split(" "):将输入的字符串按空格分割为 5 个元素的数组(对应 5 个域);
List<Integer>[] a = new ArrayList[5]:创建一个数组,数组的每个元素是List<Integer>类型(存储对应域的所有合法取值)。这里是 "数组套集合" 的结构,因为每个域的合法取值数量不固定(比如*对应所有值,,对应枚举值)。
接下来遍历每个域,解析其合法取值并存储到a[i]中:
java
运行
for(int i=0;i<s.length;i++){
a[i] = new ArrayList<>(); // 初始化当前域的List
if(s[i].contains("*")){
// 处理*:表示该域的所有可能值
for(int j=i==4||i==3?1:0;j<=unit[i];j++)
a[i].add(j);
}else if(s[i].contains(",")){
// 处理,:枚举多个值
String[] fu=s[i].split(",");
for(int j=0;j<fu.length;j++){
a[i].add(Integer.parseInt(fu[j]));
}
}else if(s[i].contains("-")){
// 处理-:范围值
String[] fu=s[i].split("-");
for(int j=Integer.parseInt(fu[0]);j<=Integer.parseInt(fu[1]);j++){
a[i].add(j);
}
}else{
// 处理单个值
a[i].add(Integer.parseInt(s[i]));
}
}
我们逐个分析4 种情况的解析逻辑:
情况 1:处理特殊字符*(所有可能值)
java
运行
if(s[i].contains("*")){
for(int j=i==4||i==3?1:0;j<=unit[i];j++)
a[i].add(j);
}
i==4||i==3?1:0:这是三元运算符,用于确定循环的起始值:
当i=3(日期域)或i=4(月份域)时,起始值为1(因为日期的取值是 1-31,月份是 1-12);
当i=0(秒)、i=1(分钟)、i=2(小时)时,起始值为0(对应取值范围 0-59、0-23);
循环范围:从起始值到unit[i](该域的最大值),将所有值加入a[i]。
例如:
若月份域是*,则i=4,循环从 1 到 12,a[4]中存储[1,2,...,12];
若秒域是*,则i=0,循环从 0 到 59,a[0]中存储[0,1,...,59]。
情况 2:处理特殊字符,(枚举值)
java
运行
else if(s[i].contains(",")){
String[] fu=s[i].split(",");
for(int j=0;j<fu.length;j++){
a[i].add(Integer.parseInt(fu[j]));
}
}
split(","):将当前域的字符串按逗号分割为多个子串(例如"2,3,20"分割为["2","3","20"]);
Integer.parseInt(fu[j]):将子串转换为整数,加入a[i]。
例如:若秒域是"2,3,20",则a[0]中存储[2,3,20]。
情况 3:处理特殊字符-(范围值)
java
运行
else if(s[i].contains("-")){
String[] fu=s[i].split("-");
for(int j=Integer.parseInt(fu[0]);j<=Integer.parseInt(fu[1]);j++){
a[i].add(j);
}
}
split("-"):将当前域的字符串按减号分割为两个子串(例如"1-5"分割为["1","5"]);
循环范围:从分割后的第一个值到第二个值,将中间的所有整数加入a[i]。
例如:若小时域是"1-31",则a[3]中存储[1,2,...,31]。
情况 4:处理单个值(无特殊字符)
java
运行
else{
a[i].add(Integer.parseInt(s[i]));
}
直接将当前域的字符串转换为整数,加入a[i]。
例如:若分钟域是"2",则a[1]中存储[2]。
步骤 2:计算 2023 年的总执行次数
解析完成后,a[0](秒)、a[1](分钟)、a[2](小时)、a[3](日期)、a[4](月份)分别存储了对应域的所有合法取值。
接下来计算总执行次数:
java
运行
int ans=0;
for(int i=0;i<a[4].size();i++){
// 当前月份:a[4].get(i)
int currentMonth = a[4].get(i);
for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[currentMonth];j++){
// 累加:秒的数量 × 分钟的数量 × 小时的数量
ans += a[0].size() * a[1].size() * a[2].size();
}
}
System.out.println(ans);
我们分层次理解这段逻辑:
第一层循环:遍历所有合法月份
java
运行
for(int i=0;i<a[4].size();i++){
int currentMonth = a[4].get(i);
// ...
}
a[4]存储的是月份域的所有合法取值(例如[1,3,5]表示 1 月、3 月、5 月);
currentMonth是当前遍历到的月份(例如1对应 1 月)。
第二层循环:遍历合法日期(需满足 "日期≤当月天数")
java
运行
for(int j=0;j<a[3].size()&&a[3].get(j)<=mon[currentMonth];j++){
// ...
}
a[3]存储的是日期域的所有合法取值(例如[1,3,15,31]);
a[3].get(j)<=mon[currentMonth]:这是关键的合法性判断 ------ 因为不同月份的天数不同(例如 2 月只有 28 天),所以需要过滤掉 "日期超过当月天数" 的情况(例如 2 月的 31 日是无效日期);
只有当日期≤mon[currentMonth](当前月份的天数)时,该日期才是合法的。
累加执行次数:时分秒的组合数
java
运行
ans += a[0].size() * a[1].size() * a[2].size();
组合数原理:对于每个 "合法的月份 + 日期",任务的执行次数等于秒的合法取值数量 × 分钟的合法取值数量 × 小时的合法取值数量(因为时分秒的取值是独立的)。
例如:
若秒域的合法取值有 3 个(a[0].size()=3),分钟域有 1 个(a[1].size()=1),小时域有 2 个(a[2].size()=2),则每个 "月 + 日" 对应的执行次数是3×1×2=6次。
四、结合题目样例验证代码逻辑
题目中给出的样例:
SimpleCron 表达式:4 21,3,15 1-31 *(注意题目中的格式可能有笔误,实际应为 5 个域,假设正确表达式是4 2 1,3,15 1-31 *);
执行次数:2023 年共执行 1095 次。
我们用代码逻辑验证:
解析各域:
秒域:"4" → a[0] = [4] → 大小为 1;
分钟域:"2" → a[1] = [2] → 大小为 1;
小时域:"1,3,15" → a[2] = [1,3,15] → 大小为 3;
日期域:"1-31" → a[3] = [1,2,...,31] → 大小为 31;
月份域:"*" → a[4] = [1,2,...,12] → 大小为 12;
计算执行次数:
对于每个月份,合法日期数为mon[currentMonth](例如 1 月有 31 天,2 月有 28 天);
每个 "月 + 日" 对应的执行次数是1×1×3=3次;
总次数 = 所有月份的合法日期数之和 × 3;
2023 年各月天数之和是31+28+31+30+31+30+31+31+30+31+30+31=365;
总次数 = 365 × 3 = 1095次(与题目样例一致)。
五、代码的优缺点与优化点
优点:
逻辑清晰:将 "解析表达式" 和 "计算次数" 分离,代码分层明确;
效率较高:通过集合存储合法取值,避免重复计算;
兼容性强:覆盖了题目要求的所有特殊字符(*、,、-)和单个值的情况。
缺点与优化点:
泛型数组的警告:List<Integer>[] a = new ArrayList[5]会产生 "未检查的转换" 警告,因为 Java 不允许直接创建泛型数组,建议改用List<List<Integer>> a = new ArrayList<>()(更符合泛型规范);
日期合法性判断的边界问题:a[3].get(j)<=mon[currentMonth]中,若a[3]包含超过 31 的值(但题目说明输入是合法的,所以风险较低);
代码命名规范:slove是拼写错误(应为solve),fu、a等变量名不直观(建议改为fields、domainValues)。
六、总结
这段代码是一个针对 SimpleCron 表达式的解析与执行次数计算工具,核心思想是:
将表达式的每个域转换为合法取值的集合(通过解析*、,、-等特殊字符);
利用组合数学原理,将 "月 - 日" 的合法组合数与 "时 - 分 - 秒" 的组合数相乘,得到总执行次数;
结合 2023 年的实际月份天数,过滤掉无效日期,确保结果的准确性。
代码通过 "数组 + 集合" 的结构灵活存储动态取值,通过双层循环遍历合法的时间维度,最终高效计算出定时任务的总执行次数,完全符合题目的要求。