news 2026/10/8 18:50:55

LeetCode 504七进制数解析:从除基取余到进制转换的通用思维

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 504七进制数解析:从除基取余到进制转换的通用思维

有一次在周赛复盘的时候,朋友问我LeetCode 504那道题是怎么写的。我说这不就是调一下Integer.toString(num, 7)么,他点点头说对啊,然后我们俩都沉默了——因为谁也说不上来,为什么七进制转换要这样写,更说不清楚这道题到底想考什么。

后来我仔细把这道题和相关的一整类进制转换题都翻了一遍,才发现这种“看起来简单到不值得写”的题,恰恰藏着计算机基础里最容易被忽略的一块拼图。LeetCode 504(Base 7)要求把给定的整数转换成七进制字符串,难度是Easy,但它的考察点远不止“会不会调用API”。如果你刚刚开始刷LeetCode,或者刷了几百题之后想回头补补基础,这篇文章应该能给你一点不一样的东西,我会把除基取余法的数学本质、负数和零值的边界陷阱、同类进制题的横向对比,以及我在实际刷题和面试别人过程中的一些体会都过一遍。

1. 一道“送分题”背后的考察意图

1.1 先看清题目本身

LeetCode 504的原题描述很简单:给定一个整数num,返回它的七进制字符串表示。输入范围是-10^7到10^7。题里给了两个示例:

  • 输入100,输出"202"。因为100 = 2×49 + 0×7 + 2,对应七进制下的202。
  • 输入-7,输出"-10"。负号直接保留,后面的数字部分照常转换。

从实现难度上看,这绝对是LeetCode里最友好的一批Easy题。核心操作只有两个:整数除法(除以7)和取余(对7取余),外加一个字符串反转。时间复杂度是O(log₇n),因为每除一次,数字规模缩小到原来的七分之一,循环进行的次数约等于n在七进制下的位数。

但问题是:如果只是背住了代码,比如记住“先循环取余、再反转、最后拼符号”,那换成八进制、九进制甚至负进制,你又会懵。所以这道题的考察意图从来不是让候选人在LeetCode上拿一道通过率很高的签名题,而是看你能不能从具体实现里抽象出“任意进制转换”的通用模型。

1.2 为什么偏偏是七进制

这是个很有意思的事。如果出题人选二进制,你可以用移位和位掩码去解,甚至很多人背过Integer.toBinaryString;选十六进制,你可以想到每次取低4位那一套位运算技巧;选八进制同理。这些进制都和计算机内部表示挂钩,即使不懂原理,靠背代码也能蒙混过关。七进制不一样,它跟2、4、8、16没有一丝一毫的位运算关联。

我自己的体会是,当你被迫处理一个和机器无关的进制时,唯一能依靠的就是数学本身:一个十进制数n在b进制下的表示,本质上就是n反复除以b留下的余数,按照从后到前的顺序拼在一起。

这条规律非常朴素,但它可以覆盖所有进制——2进制、7进制、16进制、36进制,甚至62进制和负进制。LeetCode选七进制,更像是一道“筛子题”,筛掉那些只背API不理解原理的人。

1.3 它在周赛里的定位

这类题在LeetCode周赛里通常出现在第一题,也就是大家常说的“签到题”。但是我观察过一个现象:周赛430那几次我复盘下来,T1做最快的大佬可能30秒就AC了,而很多中游选手反而会在这类题上浪费时间,原因往往就是边界处理或是对负数的处理想复杂了。这正好说明了简单题真正的意义——它不考你多会写代码,而是考你在时间压力下能不能一步到位,不犯低级错误。

简单说,看懂这道题,不只是看懂了一题,而是看懂了一类送分题的送分逻辑。下次再碰到类似的“基础题”,你就知道该往哪些地方多留个心眼。

2. 除基取余的数学原理:从短除法到三种实现

2.1 小学短除法,其实已经给了答案

想理解这道题,完全可以抛开代码,先在草稿纸上做一遍小学学过的短除法。

以100转七进制为例:

  • 100 ÷ 7 = 14,余数2
  • 14 ÷ 7 = 2,余数0
  • 2 ÷ 7 = 0,余数2

把余数按从下往上读:2、0、2,得到"202",这就是答案。

为什么是倒着读?因为除法过程是从最低位开始拆解的。十进制的202,背后的含义是2×7² + 0×7¹ + 2×7⁰;而每一次除以7的余数,正好是当前七进制位上的数字。先除出来的余数对应7⁰这一位,也就是最右边那一位;最后除出来的余数对应最高位。所以必须倒序。

这个原理适用于一切进制。同样一个数,转成二进制、八进制、十六进制,无非是把除数换成2、8、16,商为0时结束,余数倒序拼起来。理解一次,所有进制题目都能共用同一套心法,它也是这道题所有解法的地基。

2.2 写法一:内置函数一行流

Java提供了一行代码的解法:

class Solution { public String convertToBase7(int num) { return Integer.toString(num, 7); } }

Python选手就没有这个待遇了。Python标准库里的bin()、oct()、hex()只支持二进制、八进制和十六进制,没有直接输出七进制字符串的内置函数。不过手动写也就几行:

class Solution: def convertToBase7(self, num: int) -> str: if num == 0: return "0" sign = "-" if num < 0 else "" n = abs(num) res = [] while n > 0: res.append(str(n % 7)) n //= 7 return sign + ''.join(reversed(res))

这里要提醒一句:面试里直接交出内置函数,通常是一把双刃剑。一方面说明你熟悉标准库;另一方面,面试官大概率会接着追问:“那它的内部是怎么实现的?负数怎么处理的?”别到时候答不上来。所以内置函数适合快速AC,但不适合作为唯一答案。

2.3 写法二:循环取余,手动实现

手动实现是这道题的核心写法,也是我建议所有人都要写一遍的版本:

class Solution { public String convertToBase7(int num) { if (num == 0) { return "0"; } boolean negative = num < 0; int n = negative ? -num : num; StringBuilder sb = new StringBuilder(); while (n > 0) { sb.append(n % 7); n /= 7; } if (negative) { sb.append('-'); } return sb.reverse().toString(); } }

这段代码有三个细节值得说。

第一,先处理0,否则while循环一次都不进,返回空字符串。第二,符号单独记录,先把负数转成正数参与运算,最后再拼负号。第三,用append再reverse,而不是每次都insert到头部,因为StringBuilder的insert(0, c)是O(n)操作,整体会变成O(n²)。常常看到初学者在循环里写sb.insert(0, n % 7),字面上看着没问题,实测数据量大点之后性能很难看,这个习惯最好早点改过来。

复杂度方面,时间O(log₇n),空间O(log₇n),这已经是最优级别,没什么可优化的空间。

2.4 写法三:递归,让思路更接近数学定义

递归写法更短,也更贴近“把大问题拆成小问题”的数学定义:

class Solution { public String convertToBase7(int num) { if (num < 0) { return "-" + convertToBase7(-num); } if (num < 7) { return String.valueOf(num); } return convertToBase7(num / 7) + num % 7; } }

每次先递归处理num / 7,再把当前的num % 7拼上去,省去了显式反转。理解起来非常直观:七进制表示的字符串 = 高位的字符串 + 最低位的数字字符。

这里有个小细节值得品一下:递归的终止条件是num < 7,而num=0时,num < 7成立,直接返回"0",所以0也能正确处理。这个边界条件设计得很巧妙,少写了一行特判。

递归解法的劣势在于,如果进制相关的问题变形得比较深,比如把数字改成超长整数(BigInteger),迭代写法更容易控制。递归虽然优雅,但栈深等于位数,对于7进制来说最多几十层,基本不用担心栈溢出。

2.5 三种写法的取舍

实现方式代码量是否内置面试评价推荐指数
内置函数1行是简洁但容易被追问适合快速AC,不适合作为唯一答案
循环取余10行左右否稳,能展示边界意识最推荐
递归7行左右否优雅,思路清晰第二推荐

我的建议是:先写循环版本,再顺手写一个递归版本。这样既掌握了最快的思路,也在面试官面前展示了从迭代到递归的思维转换能力。这道题本身不难,但一个候选人在解法之外是否能讲清楚自己的思考过程,差距还是挺大的。

3. 边界条件才是分水岭:0、负数与溢出陷阱

3.1 0这个数字,最简单也最容易翻车

我第一次写这道题的时候,用了一个很顺手的模板:

while (n > 0) { sb.append(n % 7); n /= 7; } return sb.reverse().toString();

自以为天衣无缝,结果把0一输进去,直接返回空字符串。LeetCode判题再一看,哦,要求输出"0"。

所以0必须特判。这几乎是所有进制转换题的通病,不只是七进制。不管是LeetCode 504还是别的进制转换题,看到0就第一时间返回"0",准没错。这个习惯能帮你避开很多无谓的WA。

3.2 负数:为什么不能直接转

关于负数的处理,常规思路是:先记录符号,取绝对值去算数字部分,最后在结果前拼上负号。这个流程本身没有问题,但有一个细节容易被忽略:取绝对值这个操作,在不同语言里行为不一样。Java的Math.abs(-7)返回7,一切正常;可一旦数字范围放宽,Math.abs的结果可能出乎意料。

这道题给的约束是-10^7到10^7,所以怎么取绝对值都不会溢出。但如果你在面试中直接写出int n = Math.abs(num),面试官大概率会追问一句:“如果把输入范围改成整个int范围呢?Integer.MIN_VALUE的绝对值是多少?”

答案是这样:Integer.MIN_VALUE的绝对值还是负数。因为Integer的取值范围不对称,-2147483648取绝对值理论上应该是2147483648,但这个数超出了int的最大值2147483647,于是发生溢出,结果还是-2147483648。

3.3 Integer.MIN_VALUE:一个真实的溢出陷阱

来看一段危险代码:

int n = Math.abs(num); while (n > 0) { // 按位处理 }

当num = Integer.MIN_VALUE时,Math.abs(num)的结果是-2147483648,还是个负数。while的条件变成了-2147483648 > 0,直接不进入循环,代码就挂了。

更隐蔽的写法是下面这种:

int n = num; if (n < 0) { n = -n; }

在n = Integer.MIN_VALUE时,-n的结果同样是-2147483648。只是把问题从左边挪到了右边而已。

想稳妥处理,最简单的办法是换long类型:

long n = num; if (n < 0) { n = -n; }

long的取值范围比int大得多,Integer.MIN_VALUE的绝对值在long里完全放得下。这个防护思路很通用,凡是遇到“取绝对值再运算”的题型,能往long上想一层,就能少踩一个隐患。虽然这道题的输入范围不会触发这个坑,但作为知识点尽早掌握,能让你在变种题里多一分从容。

3.4 实际刷题时遇到过的奇怪报错

我还见过一种很奇怪的崩溃写法。有个朋友在while循环里直接用负数n取余,比如输入-7:

  • 第一次循环:-7 % 7 = 0,append了0;
  • -7 / 7 = -1;
  • 第二次循环:-1 % 7 = -1(Java里负数取余结果也是负),append了-1;
  • -1 / 7 = 0,循环结束;
  • 反转结果变成"0-1",完全错乱。

Java对负数的整数除法是向零取整,取余的符号跟随被除数,所以不要指望负数直接参与进制转换还能得到正确结果。先取绝对值再运算,是最省心的路线。这个原则同样适用于其他进制转换题,记住了能省不少debug时间。

3.5 StringBuilder的拼接技巧

再补充一个有关字符串拼接的小细节。网上很多题解习惯在循环里写成:

sb.insert(0, digit);

看着很简单,但insert(0, ...)每次都要把已有字符整体往后挪,复杂度是O(n)。数据只有两三位的时候当然无所谓,可一旦碰到那种要你转成几百位字符串的变形题,性能差距就会很明显。

正确习惯是先append到末尾,最后统一reverse。一行reverse的时间成本远低于每次insert的开销。这个习惯在同类型的LeetCode 7(整数反转)里也一样适用,可以说是极其通用的优化技巧。

4. 从504延伸到同类题型:进制转换家族横向对比

4.1 位运算视角:为什么2、8、16进制特殊

聊完504,我想把视角拉高一点。2、8、16进制之所以能在程序里经常出现,是因为它们全部是2的幂。2⁴=16、2³=8,所以每个十六进制位恰好对应4个二进制位,每个八进制位对应3个二进制位。这给了我们极大的便利——可以用移位和掩码快速搞定转换。

比如LeetCode 405(数字转十六进制),核心做法就是每次取num的低4位:

class Solution { public String toHex(int num) { if (num == 0) { return "0"; } char[] digits = "0123456789abcdef".toCharArray(); StringBuilder sb = new StringBuilder(); while (num != 0) { sb.append(digits[num & 15]); num >>>= 4; // 无符号右移 } return sb.reverse().toString(); } }

这里的关键是num >>>= 4,逻辑右移会把高位补0,所以-1都能被正确转成"ffffffff"。这题本质上考的是无符号右移和位掩码,和504考的是两套知识体系,但都属于进制转换这个大家族。

七进制不是2的幂,所以走不了位运算这条路,只能老老实实除7取余。某种意义上,LeetCode 504的价值就在于逼着你放弃捷径,回到数学模型本身。

4.2 同类题快速对比

题目考点与504的异同解题要点
504 七进制数通用进制转换基准题除基取余,负号单独处理
168 Excel表列名称26进制变体进制数字从1开始,没有0每次先减1再取余
171 Excel表列序号26进制逆运算504的反向操作从左往右迭代乘26
405 数字转十六进制位运算负数需要无符号右移每4位一组,num & 15
67 二进制求和进位与字符串进制运算而非转换从低位往高位逐位加,维护carry

这里面最值得多说两句的是LeetCode 168。它表面上也是转进制,但Excel的列名是A到Z,对应1到26,没有0这个数字。这意味着不能直接用除26取余那一套,得先做一步n--把问题从1-based变成0-based,再套通用模板。很多人第一次写168都会栽在这个偏移上,把504的模板原样套过去,处理边界时就会出错。

这种对比正好证明了:进制转换看似可以套一个模板,但细节一变,模板就失效了。所以真正值得刷的不是某道题的答案,而是那层“先理解数字本质,再套用数学规律”的思考方式。

4.3 反馈到一个更广的应用场景

进制转换也不只是刷题里的玩具。真实工程里,短链接服务就经常把自增ID转成62进制字符串来缩短URL;颜色值在CSS里就是十六进制;Base64本质上可以理解成一种“64进制”编码;权限系统里的位掩码是二进制的直接应用。把504这类题吃透,以后碰到这些场景,你会比别人多一层底层理解。

面试时我也见过不少候选人拿这道题当热身,然后被追问到“短链接为什么要用62进制而不是64进制”时卡住。这类延伸问题不在LeetCode的AC判题范围内,但恰恰是区分“刷题机器”和“真正理解基础”的分界线。

5. 我对这道题的刷题方法与实践建议

5.1 简单题的正确打开方式

刷简单题最常见的误区是“AC了就跑”。特别是热门一百题里那些Easy题,很多人直接跳过,觉得不值得花时间。以我自己的经验,像504这道题,如果不额外做一些变形练习,价值会大打折扣。

我的做法是这样的:AC之后,立刻逼自己在脑子里做三个变体:

  1. 把7换成任意b(b在2到36之间),重新实现一遍;
  2. 把int改成long甚至BigInteger,看代码哪里会出问题;
  3. 写一个逆函数,把七进制字符串解析回十进制整数。

这三步做完,你对进制转换这个知识点的掌握程度基本可以覆盖80%的同类面试题。不是夸张,很多Easy题真正值得学习的部分,恰恰是AC之后那半个小时的延伸思考。

5.2 如果在面试现场遇到这道题

面试时如果你遇到这道题,我建议按这个顺序回答:

  • 先介绍思路:这是除基取余法,一个数在b进制下的表示由反复除法得到的余数逆序构成;
  • 再写代码:写循环版本,主动处理0和负数;
  • 最后补一句边界考虑:如果输入扩展到int全范围,需要小心Integer.MIN_VALUE取绝对值溢出的问题,可以用long来规避。

这一套下来,你不仅把题做了,还把面试官想考察的数学原理、边界意识和语言特性全部覆盖了。对于一道Easy题来说,这已经是满分级表现。

反过来,如果上来就直接写return Integer.toString(num, 7),大概率会有连续追问,一旦答不好反而扣分。不是说内置函数不能用,而是最好把它放在手动实现的后面,作为“如果标准库支持,其实一行也能搞定”的补充发言,效果会好很多。

5.3 周赛与日常刷题的一个小习惯

最后分享一个我最近在周赛430复盘后养成的习惯:每场周赛的T1不管多简单,我都不会只看自己AC没AC,而是去翻翻大佬们的写法。同样是签到题,有人写递归,有人用内置函数,有人一行流。每一次对比,都能看到新的写法思路,这是单纯刷题数量给不了的东西。

LeetCode 504这道题,我看过不下20种写法,最长的题解会把负数、零值、大数、内置函数、递归、迭代全部讲一遍。不是说写得越长越好,而是说明:一道再简单的题,也藏着足够多的知识点值得反复咀嚼。

提示:刷题不是比拼AC数量,而是比拼对基础知识的融会贯通。一道Easy题如果能讲清楚“为什么”三个字,它的价值就能超过十道难题的无脑AC。

我自己最近在复习基础算法时,也习惯拿这类题当自测卷:不用IDE、不用内置函数,直接在白纸上把代码写出来,再口头讲一遍原理。如果讲得顺,说明基础还算扎实;如果卡壳了,就说明某个环节只是“看着会”而已。LeetCode 504就是个不错的自测题,因为它足够简单,简单到能暴露你思维里的所有漏洞。把这个漏洞补上,才算真正吃透了这道题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 18:45:50

一文讲透|2026年最火AI论文平台榜单,毕业论文免费写还合规

2026 年实测 10 款主流 AI 论文工具&#xff0c;千笔AI以全流程覆盖 语义级降重 免费查重领跑综合榜&#xff1b;ThouPen 稳坐留学生毕业全流程工具头把交椅&#xff1b;免费工具中DeepSeek Scholar、豆包学术版表现亮眼&#xff0c;30 分钟即可生成万字高质量初稿&#xff0…

作者头像 李华
网站建设 2026/10/8 18:45:01

基于Jsp+Servlet的医院预约挂号系统实现要点解析

简介&#xff1a;基于JspServlet构建的医院预约挂号系统完整项目包&#xff0c;面向JavaWeb初学者、课程设计与毕业设计开发者&#xff0c;帮助解决从环境搭建、源码部署到前后端功能梳理的全流程问题。前端提供首页展示、用户注册、医生查询与公告信息等基础功能&#xff1b;后…

作者头像 李华