LeetCode 补充题 14. 阿拉伯数字转中文数字
题目描述
:::fold-green 相关原题
✅ 阿拉伯数字转中文
牛客包含负数输入,本文实现的范围为非负 32 位整数。
:::
给定非负整数
num,请返回它的中文读法。采用普通中文数字和数位单位,使用“二”而不是“两”,不使用财务大写。连续的缺位零只读一次,末尾的零不读;整数
0本身写作“零”。
示例 1:
输入:num = 1234
输出:"一千二百三十四"
示例 2:
输入:num = 10001
输出:"一万零一"
提示:
-
0 <= num <= 2^31 - 1。 - 每四位为一节,节内使用“十、百、千”,节外使用“万、亿”。
- 整串以“一十”开头时省略开头的“一”,例如
10写作“十”。
题意分析
将数字转换成带有中文数位单位的读法,不是简单地把每个阿拉伯数字替换成汉字。连续的零只读一次,末尾的零不读;中文按四位分节,节内使用千、百、十,节外使用万、亿。
以下实现处理非负 32 位整数,最多涉及个、万、亿三个节,数字零本身返回
零。采用“二”和普通数字读法,不涉及负数、小数或财务大写。需要分别处理节内缺位、节与节之间缺位,以及整串开头一十中“一”的省略。
解法:四位分组转换
核心思路
[!blue]
中文大单位每四位变化一次,所以先按
10000拆成三个节,再从高节到低节拼接。每一节都不超过四位,可以用同一套千、百、十、个的规则转换,最后加上这一节对应的万或亿。节内从高位到低位读取。遇到非零数字就输出“数字 + 数位单位”;遇到零时先不输出,只用
zeroPending记住“已有非零内容后出现了空缺”。后续再次遇到非零数字,才补一个零并清除标记。连续零只留下一个标记,末尾零则因为没有后续非零数字而永远不输出;节前面的零也不会建立这个标记。节间用另一个标记
needZero处理较大的空缺。高节已经输出后,若跳过了全零的中间节,就记录需要补零;即使没有空节,后面的非零节不足四位,也说明它与前一节之间缺少数位。因此,输出下一个非零节前,只要“跳过空节”或“当前节小于1000”成立,就补一个零。两个条件同时成立也只补一次。全零节不直接输出,否则会带上不需要的大单位或尾零。最后一个非零节处理完后,后面即使还有空节,也只留下未使用的标记,不会给结果增加尾零。节内转换不会产生首零或尾零,因而与节间补零衔接时不会出现重复零。
各节先统一生成完整的
一十,拼完整个结果后,只在整串以一十开头时去掉第一个“一”。不能让每个节自行省略,否则低节中的十位会失去应有的完整读法。
解题步骤
- 输入为
0时直接返回零。- 按四位拆出个节、万节、亿节,从亿节向个节遍历。
- 空节跳过;若此前已有输出,设置节间待补零标记。
- 处理非零节时,先根据空节标记和当前节是否不足四位决定是否补零。
- 在该节内部依次读取千、百、十、个,延迟补零,只为非零位写出数字及单位。
- 拼接节单位,清除节间标记;最后仅省略整串开头
一十中的“一”。
代码实现
class Solution {
private static final String[] DIGITS = {
"零",
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九"
};
private static final String[] UNITS = {
"千",
"百",
"十",
""
};
private static final String[] GROUPS = {
"",
"万",
"亿"
};
public String numberToChinese(int num) {
if (num == 0) {
return "零";
}
int[] parts = {
num % 10000,
num / 10000 % 10000,
num / 100000000
};
StringBuilder builder = new StringBuilder();
boolean needZero = false;
for (int group = 2; group >= 0; group--) {
if (parts[group] == 0) {
if (builder.length() > 0) {
needZero = true;
}
continue;
}
// 跨过空节或低节不足四位时,在下一段非零内容前补零。
if (builder.length() > 0 && (needZero || parts[group] < 1000)) {
builder.append("零");
}
builder.append(convertGroup(parts[group])).append(GROUPS[group]);
needZero = false;
}
String result = builder.toString();
// 只省略整串开头的一,不能在每个节内独立省略。
if (result.startsWith("一十")) {
result = result.substring(1);
}
return result;
}
private String convertGroup(int value) {
int[] divisors = {
1000,
100,
10,
1
};
StringBuilder builder = new StringBuilder();
boolean zeroPending = false;
for (int i = 0; i < divisors.length; i++) {
int digit = value / divisors[i];
value %= divisors[i];
if (digit == 0) {
zeroPending = builder.length() > 0;
continue;
}
// 节内的零延迟到下一个非零位输出,避免连续零和尾零。
if (zeroPending) {
builder.append("零");
zeroPending = false;
}
builder.append(DIGITS[digit]).append(UNITS[i]);
}
return builder.toString();
}
}
var chineseDigits = []string{
"零",
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九",
}
var chineseUnits = []string{
"千",
"百",
"十",
"",
}
var chineseGroups = []string{
"",
"万",
"亿",
}
func numberToChinese(num int) string {
if num == 0 {
return "零"
}
parts := []int{
num % 10000,
num / 10000 % 10000,
num / 100000000,
}
result := ""
needZero := false
for group := 2; group >= 0; group-- {
if parts[group] == 0 {
if len(result) > 0 {
needZero = true
}
continue
}
// 跨过空节或低节不足四位时,在下一段非零内容前补零。
if len(result) > 0 && (needZero || parts[group] < 1000) {
result += "零"
}
result += convertChineseGroup(parts[group]) + chineseGroups[group]
needZero = false
}
// 只省略整串开头的一,不能在每个节内独立省略。
if len(result) >= len("一十") && result[:len("一十")] == "一十" {
result = result[len("一"):]
}
return result
}
func convertChineseGroup(value int) string {
divisors := []int{
1000,
100,
10,
1,
}
result := ""
zeroPending := false
for i, divisor := range divisors {
digit := value / divisor
value %= divisor
if digit == 0 {
zeroPending = len(result) > 0
continue
}
// 节内的零延迟到下一个非零位输出,避免连续零和尾零。
if zeroPending {
result += "零"
zeroPending = false
}
result += chineseDigits[digit] + chineseUnits[i]
}
return result
}
复杂度分析
- 时间复杂度:$O(1)$。限定非负 32 位整数,最多处理三个节,每节只扫描四个数位。
- 空间复杂度:$O(1)$。数字表、单位表、分节数组和结果长度都有固定上界。
关键点总结
[!green]
- 四位分节把大数读法分成“节内转换”和“节间连接”两个独立问题。
- 零只先记状态,后续非零内容出现时才输出,同时解决连续零和尾零。
- 节间补零既要考虑跳过空节,也要考虑低节不足四位。
一十的省略只作用于最终结果开头。
易错点总结
[!yellow]
- 输入零若不特判,所有节都会被跳过,结果会成为空字符串。
- 一遇到零就输出,会产生连续零或尾零;应等到后续非零内容出现再补。
- 节间只检查是否有全零节,会漏掉非零低节不足四位时需要补的零。
- 每个节都省略开头的“一”,会错误改变低位节的完整读法。
- 当前接口及分节单位只覆盖声明的非负 32 位范围,不能直接套用到负数或任意长度整数。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 补充题 13. 中文数字转阿拉伯数字 | 中等 | 原题按单位解析为64位数值,本题以非负32位整数按四位节输出,往返输入需在共同范围内,零处理规则相对应。 |
| 273. 整数转换英文表示 | 困难 | 同样将整数分组转成自然语言读法,本题是四位一节,英文是三位一组。 |
| 补充题 200. 十进制数的中文读法 | 中等 | 整数部分可复用四位分节与补零规则;该题还需逐位读取小数,并输出小数点后的零,本题仅处理非负整数。 |