【算法实战】日期转二进制:两种解法的思路与优化(附代码解析)
一、问题描述
给定一个yyyy-mm-dd
格式的日期字符串,要求将年、月、日分别转为无前导零的二进制,并保持year-month-day
格式。
示例:输入2025-03-15
,输出11111101001-11-1111
(2025→11111101001,3→11,15→1111)。
二、解法一:直接分割转换(新手友好)
思路分析
- 分割日期:按
-
拆分为年、月、日三部分。 - 转二进制:利用
Integer.toBinaryString
直接转换。 - 拼接结果:按格式拼接,无需处理前导零(Java 方法自动省略)。
代码实现
class Solution {
public String convertDateToBinary(String date) {
String[] parts = date.split("-"); // 分割日期
return Integer.toBinaryString(Integer.parseInt(parts[0])) + "-" +
Integer.toBinaryString(Integer.parseInt(parts[1])) + "-" +
Integer.toBinaryString(Integer.parseInt(parts[2]));
}
}
代码亮点
- 简洁直观:一行代码完成核心逻辑,适合快速开发。
- 自动去前导零:Java 的
toBinaryString
天然支持,无需额外处理。
复杂度分析
- 时间:O (1)(日期长度固定,拆分和转换为常数操作)
- 空间:O (1)(除输入外,额外空间固定)
三、解法二:逆向字符填充(性能优化)
思路升级
观察发现:日期各部分的二进制长度有限(年≤13 位,月≤4 位,日≤5 位),可预先分配固定大小的字符数组,从后向前填充,避免字符串拼接的性能损耗。
关键步骤拆解(以2025-03-15
为例)
- 提取日(15):转为二进制
1111
,逆向填充到数组末尾。 - 插入
-
:在日的二进制前添加分隔符。 - 提取月(3):转为
11
,填充到-
前。 - 插入
-
:在月的二进制前添加分隔符。 - 提取年(2025):转为
11111101001
,填充到最前面。
代码实现(含详细注释)
class Solution {
private static final int MAX_LEN = 32; // 足够容纳最大可能的二进制(年13位 + 2个- + 月4位 + 日5位 = 25位)
public String convertDateToBinary(String date) {
char[] newDate = new char[MAX_LEN]; // 预分配固定大小数组
int begin = MAX_LEN; // 从数组末尾开始填充
// 处理日(索引8-9,即"dd")
begin = appendBinary(date, newDate, begin, 8, 9);
newDate[--begin] = '-'; // 插入日-月分隔符
// 处理月(索引5-6,即"mm")
begin = appendBinary(date, newDate, begin, 5, 6);
newDate[--begin] = '-'; // 插入月-年分隔符
// 处理年(索引0-3,即"yyyy")
appendBinary(date, newDate, begin, 0, 3);
// 从begin到数组末尾的有效字符转为字符串
return new String(newDate, begin, MAX_LEN - begin);
}
private int appendBinary(String date, char[] arr, int ptr, int start, int end) {
int num = 0;
// 1. 提取日期部分并转为整数(如"2025"→2025)
for (int i = start; i <= end; i++) {
num = num * 10 + (date.charAt(i) - '0');
}
// 2. 整数转二进制,逆向填充字符数组
while (num > 0) {
arr[--ptr] = (char) ((num % 2) + '0'); // 取最低位,从后往前填
num /= 2;
}
return ptr; // 返回新的填充起点
}
}
核心优化点
- 避免字符串拼接:通过字符数组直接操作,减少
String
对象创建开销。 - 逆向填充:二进制低位在后(如 15→1111,填充顺序是
1→1→1→1
),天然符合数组从后往前填充的逻辑。 - 固定长度数组:预先计算最大可能长度(32 位足够),空间利用更高效。
四、两种解法对比
维度 | 解法一(分割转换) | 解法二(逆向填充) |
---|---|---|
代码行数 | 简洁(1 行核心逻辑) | 复杂(需处理索引和填充) |
性能 | 适用于大多数场景 | 更优(减少字符串操作) |
可读性 | 直观,适合新手 | 需理解逆向填充逻辑 |
适用场景 | 快速开发、小规模数据 | 高频调用、性能敏感场景 |
五、测试用例与输出
输入日期 | 二进制表示 | 说明 |
---|---|---|
2025-03-15 | 11111101001-11-1111 | 正常日期,包含非整十数 |
2000-01-01 | 11111010000-1-1 | 月 / 日为个位数 |
1970-01-01 | 1111001110-1-1 | 早期年份,二进制较短 |
9999-12-31 | 10011100001111-1100-11111 | 最大可能日期(年 9999) |
六、总结
- 核心考点:日期解析、二进制转换、字符串操作优化。
- 逆向思维:通过预分配数组和逆向填充,避免字符串拼接的性能损耗,这是处理固定格式字符串的常用技巧。
- 扩展性:若需处理大量日期(如日志批量转换),解法二更具优势;日常开发中解法一的简洁性更值得优先选择。
代码启示:编程中往往有多种解法,选择时需权衡可读性、性能和场景需求,这正是算法优化的魅力所在。