- 示例工程
【免费下载链接】leetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
本文基于 doocs/leetcode 仓库中 0168.Excel Sheet Column Title 英文题解(中文题解)展开。这道题表面是「进制转换」,实则是数字系统(number system)中的一个经典陷阱:Excel 列名使用的并不是我们熟悉的普通 26 进制,而是一种没有 0 数字的「双射 26 进制」(bijective base-26)。读完本文,你将掌握:为什么26 -> "Z"而不是"A0"、如何用「先减一再取模」的统一写法规避余 0 问题,以及 Python3 / Java / Go / TypeScript / Rust / C# 六种语言的实现差异与边界处理。
一、题目回顾:整数到列名的映射规律
题目要求:给定整数columnNumber(取值范围1 <= columnNumber <= 2^31 - 1),返回它在 Excel 表格中对应的列名称。
映射规律如下:
A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ...题目给出的三个标准示例:
输入columnNumber | 输出 |
|---|---|
| 1 | "A" |
| 28 | "AB" |
| 701 | "ZY" |
此外,仓库中的中文题解还给出了一个最大边界示例,便于验证算法在 32 位整数上限处的正确性:
输入columnNumber | 输出 |
|---|---|
| 2147483647(即 2^31 - 1) | "FXSHRXW" |
二、核心难点:Excel 列名不是普通 26 进制
这是本题最容易踩坑的地方。如果按照「普通 26 进制」来思考,A~Z应该对应0~25,那么26会被写成"A0"、27写成"A1"……但这显然与题目给出的映射Z -> 26、AA -> 27矛盾。
正确理解是:Excel 列名是一个「没有 0」的进制系统,26 个字母A~Z充当的是1~26这 26 个「非零数字」,而不是0~25:
- 一位数:
A(1) ~ Z(26); - 两位数:
AA(27) ~ ZZ(702); - 三位数:
AAA(703) ~ ZZZ(18278),以此类推。
原题解中的思考(thinking)段落对此有精辟总结:"Ordinary base 26 would emit 0 instead of Z on a zero remainder"——普通 26 进制在余数为 0 时会输出0,而这里必须输出Z。因此核心解法是:每次先减 1,再对 26 取模,把余数映射到A~Z,商继续迭代,最后反转字符序列。
三、标准解法:先减一再取模(双射 26 进制)
算法流程(伪代码):
while columnNumber > 0: columnNumber = columnNumber - 1 当前字符 = 'A' + columnNumber % 26 将当前字符加入结果(头部插入或最后统一反转) columnNumber = columnNumber // 26 反转结果,返回为什么「先减一」能解决问题?因为在双射进制中,数字范围是1~26,而取模运算天然工作在0~25上。减一操作把数字空间平移一位,使余数0~25恰好对应A~Z,同时商也相应调整,保证进位边界(如26 -> Z、27 -> AA)正确。
逐步推演示例:columnNumber = 701(应输出"ZY")
701 - 1 = 700,700 % 26 = 24→ 字符'A' + 24 = 'Y';700 // 26 = 26;26 - 1 = 25,25 % 26 = 25→ 字符'A' + 25 = 'Z';25 // 26 = 0,循环结束;- 逆序拼接得到
"ZY"。✔
四、多语言实现与源码对照
本仓库为本题提供了独立的 Solution 源码目录,包含 Python3、Java、Go、TypeScript、Rust、C# 六个实现文件,与题解文档中的代码一一对应,可直接运行验证。
4.1 Python3
对应源码:Solution.py
class Solution: def convertToTitle(self, columnNumber: int) -> str: res = [] while columnNumber: columnNumber -= 1 res.append(chr(ord('A') + columnNumber % 26)) columnNumber //= 26 return ''.join(res[::-1])要点:用列表收集每次计算出的字符(低位在前),最后通过res[::-1]一次反转完成拼接。chr(ord('A') + columnNumber % 26)将余数0~25映射到A~Z。
4.2 Java
对应源码:Solution.java
class Solution { public String convertToTitle(int columnNumber) { StringBuilder res = new StringBuilder(); while (columnNumber != 0) { --columnNumber; res.append((char) ('A' + columnNumber % 26)); columnNumber /= 26; } return res.reverse().toString(); } }要点:StringBuilder天然支持reverse(),字符以char强转方式从'A' + 余数得到,最终调用res.reverse().toString()输出正确顺序。
4.3 Go
对应源码:Solution.go
func convertToTitle(columnNumber int) string { res := []rune{} for columnNumber != 0 { columnNumber -= 1 res = append([]rune{rune(columnNumber%26 + int('A'))}, res...) columnNumber /= 26 } return string(res) }要点:这里采用头插法,append([]rune{...}, res...)把新字符插到切片头部,从而免去最后反转的步骤。rune类型用于安全处理 Unicode 字符,int('A')拿到A的 ASCII 码 65。
4.4 TypeScript
对应源码:Solution.ts
function convertToTitle(columnNumber: number): string { let res: string[] = []; while (columnNumber > 0) { --columnNumber; let num: number = columnNumber % 26; res.unshift(String.fromCharCode(num + 65)); columnNumber = Math.floor(columnNumber / 26); } return res.join(''); }要点:String.fromCharCode(num + 65)用65(A的 ASCII 码)代替'A',效果等价;unshift为头插法;注意使用Math.floor处理整除,避免浮点精度问题。
4.5 Rust(特判写法)
对应源码:Solution.rs
impl Solution { #[allow(dead_code)] pub fn convert_to_title(column_number: i32) -> String { let mut ret = String::from(""); let mut column_number = column_number; while column_number > 0 { if column_number <= 26 { ret.push((('A' as u8) + (column_number as u8) - 1) as char); break; } else { let mut left = column_number % 26; left = if left == 0 { 26 } else { left }; ret.push((('A' as u8) + (left as u8) - 1) as char); column_number = (column_number - 1) / 26; } } ret.chars().rev().collect() } }要点:这是与「先减一再取模」等价但写法不同的另一种实现。它显式处理余 0 情况:当column_number % 26 == 0时,把余数修正为26(即字母Z),同时用(column_number - 1) / 26得到下一轮商。当column_number <= 26时直接输出对应字母并终止循环,最后ret.chars().rev().collect()反转。两种思路殊途同归,Rust 版本更直观地体现了「余 0 补 26」的双射进制语义。
4.6 C#
对应源码:Solution.cs
public class Solution { public string ConvertToTitle(int columnNumber) { StringBuilder res = new StringBuilder(); while (columnNumber != 0) { --columnNumber; res.Append((char) ('A' + columnNumber % 26)); columnNumber /= 26; } return new string(res.ToString().Reverse().ToArray()); } }要点:与 Java 版几乎一致的逻辑,差异仅在于 C# 的StringBuilder没有内建Reverse(),因此借助 LINQ 的Reverse()扩展方法配合ToArray()完成反转,再包装为string。
五、复杂度与边界用例分析
复杂度:每次循环columnNumber至少缩小 26 倍,因此:
- 时间复杂度:
O(log_26 columnNumber),即结果字符串的长度; - 空间复杂度:
O(log_26 columnNumber),用于存储结果字符。
关键边界用例自检表(建议写代码时逐一验证):
| 输入 | 输出 | 说明 |
|---|---|---|
| 1 | "A" | 最小值,一位字母 |
| 26 | "Z" | 最重要的进位边界:余 0 场景,普通 26 进制会错算成"A0" |
| 27 | "AA" | 进位后回到A开头 |
| 52 | "AZ" | 第二位是Z的场景 |
| 701 | "ZY" | 题目标准示例 |
| 702 | "ZZ" | 两位数最大值 |
| 703 | "AAA" | 进入三位数 |
| 2147483647 | "FXSHRXW" | 2^31 - 1,32 位整数上限 |
六、关联拓展:逆问题 171「Excel 表列序号」
理解本题的最好方式之一是研究它的逆运算:给定列名"AB",求列序号28。仓库中收录了 0171.Excel Sheet Column Number 题解,其核心递推式为:
ans = ans * 26 + (c - 'A' + 1)即从左到右扫描字符,把每个字母看成双射 26 进制的一位数字(A~Z对应1~26),边读边累乘。例如"AB":ans = 1 * 26 + 2 = 28。该题解中明确写道:"Title to number is the inverse of problem 168"(列名转数字正是 168 题的逆运算),两个题解互为镜像,对比阅读可以彻底打通「双射进制」这一概念:168 题是「数字 → 字符串」的反复除 26,171 题是「字符串 → 数字」的反复乘 26。
七、小结
- Excel 列名的本质是没有 0 数字的 26 进制(双射进制),字母
A~Z对应1~26; - 统一解法:每轮先
columnNumber -= 1再取模 26,余数映射到A~Z,商继续迭代,最后反转; - 仓库提供了 题解文档(含六个语言标签页)与 独立 Solution 源码 双重载体,
Solution.rs还展示了「余 0 补 26」的等价特判写法; - 与逆问题 171. Excel Sheet Column Number 对照学习,可完整掌握这类「双射进制」转换题的两种方向。
掌握这道题后,你不仅能秒杀同类进制映射题,还能理解诸如「Excel 列号 ↔ 列名转换」「短网址 ID 混淆」等真实业务场景背后的同一个数学原理。
- 示例工程
【免费下载链接】leetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
相关推荐
LeetCode 168. Excel Sheet Column Title:Go 短除法实现 Excel 列名与 26 进制转换
LeetCode 168. Excel Sheet Column Title:Go 短除法实现 Excel 列名与 26 进制转换 导读 本文围绕 LeetCo
示例工程doocs/leetcode 题解 171:Excel 表列序号的 26 进制转换实现
doocs/leetcode 题解 171:Excel 表列序号的 26 进制转换实现 本篇基于 doocs/leetcode 仓库中 0171.Excel S
示例工程宫水三叶刷题日记:LeetCode 168 Excel表列名称——从 1 开始的 26 进制模拟转换全解
宫水三叶刷题日记:LeetCode 168 Excel表列名称——从 1 开始的 26 进制模拟转换全解 本篇技术指南基于仓库 LeetCode/161 170
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考