如何把整数转换成英文单词?(LeetCode 273)
简化版
整数转英文单词的核心是按三位一组拆分:Billion、Million、Thousand、最后一组。每组数字范围是 0 到 999,可以写一个 helper(num) 转成英文:小于 20 查表,小于 100 用十位表加个位,100 到 999 用 X Hundred 加余数。最后按大单位从高到低拼接,跳过值为 0 的组。
例如 1234567 拆成 1 Million、234 Thousand、567,结果是 One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven。
详细版
class Solution {
private final String[] below20 = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
"Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
"Sixteen", "Seventeen", "Eighteen", "Nineteen"};
private final String[] tens = {"", "", "Twenty", "Thirty", "Forty", "Fifty",
"Sixty", "Seventy", "Eighty", "Ninety"};
public String numberToWords(int num) {
if (num == 0) return "Zero";
int[] values = {1000000000, 1000000, 1000, 1};
String[] units = {"Billion", "Million", "Thousand", ""};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (num >= values[i]) {
int part = num / values[i];
num %= values[i];
sb.append(helper(part)).append(units[i].isEmpty() ? "" : " " + units[i]).append(" ");
}
}
return sb.toString().trim();
}
private String helper(int num) {
if (num == 0) return "";
if (num < 20) return below20[num];
if (num < 100) return (tens[num / 10] + " " + helper(num % 10)).trim();
return (below20[num / 100] + " Hundred " + helper(num % 100)).trim();
}
}
重点是把大问题拆成“每三位一组”和“0 到 999 的小转换”。字符串拼接后要处理多余空格。
完整版教学
一、为什么按三位分组
英文数字的大单位是每三位一级:Thousand=10^3,Million=10^6,Billion=10^9。因此整数可以按三位一组拆开,每组内部用同一套 0 到 999 的规则转换。
1,234,567
1 -> One Million
234 -> Two Hundred Thirty Four Thousand
567 -> Five Hundred Sixty Seven
这样不用为每个数位写大量分支,逻辑会非常稳定。
二、0 到 999 的转换规则
helper(num) 只负责转换 1..999:
1..19:英文有特殊单词,直接查表。20..99:十位查表,再递归处理个位。100..999:百位查表,加Hundred,再递归处理余数。
567
5 Hundred + helper(67)
67 = Sixty + helper(7)
7 = Seven
=> Five Hundred Sixty Seven
这个递归深度很小,最多拆百位、十位、个位。
三、大单位循环如何拼接
主函数按 Billion -> Million -> Thousand -> 个位组 处理:
int[] values = {1000000000, 1000000, 1000, 1};
String[] units = {"Billion", "Million", "Thousand", ""};
如果 num >= values[i],说明当前单位有非零组。先取 part = num / values[i],把这组三位以内的数字转英文,再追加单位名,最后 num %= values[i] 继续处理剩余部分。
四、为什么要跳过 0 组
英文数字不会读出中间的零组。例如 1000010 是 One Million Ten,不是 One Million Zero Thousand Ten。因此只有当某个组的 part > 0 时才拼接。
| 数字 | 分组 | 正确输出 |
|---|---|---|
| 1000 | 1,000 | One Thousand |
| 1000010 | 1,000,010 | One Million Ten |
| 1000000001 | 1,000,000,001 | One Billion One |
零组跳过是本题字符串正确性的关键。
五、空格处理是工程细节
递归拼接时很容易产生多余空格。常见做法是每个片段末尾允许追加一个空格,最后 trim();或者用列表收集非空片段,最后 String.join(" ", parts)。
"Twenty " + helper(0) 可能得到 "Twenty "
trim 后变成 "Twenty"
面试中要说明自己有意识地处理空格,而不是让输出依赖偶然格式。
六、特殊数字 0
0 不能交给主循环处理,因为所有单位组都会被跳过,最后得到空字符串。应当在入口直接返回 "Zero"。
if (num == 0) return "Zero";
这是一类典型的“空结果不等于正确结果”的边界条件。
七、复杂度分析
对 32 位整数来说,最多处理 Billion, Million, Thousand, 个位组 四组,每组递归深度固定,因此时间复杂度和空间复杂度都可视为 O(1)。如果泛化到任意长数字字符串,则时间复杂度与分组数量成正比。
八、递归 helper 的返回值约定
helper 最好约定为“返回没有首尾多余空格的英文片段”。这样主函数只负责在片段和单位之间加一个空格,最后再整体 trim。如果 helper 可能返回带前导空格或尾随空格的字符串,主流程会很难稳定控制格式。
helper(100) -> "One Hundred"
helper(101) -> "One Hundred One"
helper(110) -> "One Hundred Ten"
helper(1000) 不应该被调用,因为 helper 只处理三位组
这个约定能把问题边界压得很清楚:大单位由主函数处理,三位以内由 helper 处理,二者不互相抢职责。
九、为什么不用在英文里补 and
有些英语口语会说 One Hundred and One,但 LeetCode 273 的标准输出不包含 and,即 One Hundred One。因此实现时不要凭自然语言习惯额外插入单词,否则格式会错。
101
题目期望: One Hundred One
不要输出: One Hundred and One
面试中可以主动说明:本题不是做自然语言生成,而是按平台规定的格式做确定性转换。
十、从样例到代码的拆分路线
可以把 1234567891 作为完整手推样例:
1,234,567,891
1 -> One Billion
234 -> Two Hundred Thirty Four Million
567 -> Five Hundred Sixty Seven Thousand
891 -> Eight Hundred Ninety One
拼接后得到 One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One。这个样例同时覆盖 Billion、Million、Thousand、Hundred、十位和个位,是很好的自测用例。
八、常见误区与追问
- 误区:逐位从左到右硬编码。 英文单位按三位分组,逐位硬编码会非常复杂且容易漏零。
- 误区:没有特判 0。 主循环会跳过所有组,返回空字符串。
- 误区:把 10 到 19 按十位加个位处理。
Eleven、Twelve、Thirteen等必须查特殊表。 - 误区:输出中间零组。
One Million Ten中不应该出现Zero Thousand。 - 追问:为什么 LeetCode 输入只到 Billion? 因为 32 位 int 最大约 2.1 billion,单位到 Billion 足够。
- 追问:如何支持更大整数? 把输入当字符串按三位切分,并扩展单位数组即可。
十一、加强记忆
- 英文数字每三位换一个大单位。
- 主函数拆
Billion / Million / Thousand / 个位组。 helper只管1..999,分成<20、<100、>=100。- 零组跳过,数字 0 单独返回
Zero。 - 字符串拼接要收尾去空格,避免格式错误。