news 2026/9/17 22:13:02

LeetCode 阿姆斯特朗数(Armstrong Number)判定:三种数位长度 k 的计算方案与多语言实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 阿姆斯特朗数(Armstrong Number)判定:三种数位长度 k 的计算方案与多语言实现

LeetCode 阿姆斯特朗数(Armstrong Number)判定:三种数位长度 k 的计算方案与多语言实现

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

本篇文章围绕 LeetCode 1134 号题目 Armstrong Number(阿姆斯特朗数)展开,完整讲解"数位提取 + 按位求幂求和"的核心思路,并给出字符串转换、对数运算、纯算术循环三种计算数位长度k的实现方案。文中代码覆盖 Python、Java、C++、JavaScript、C#、Go、Kotlin、Swift、Rust 九种语言,全部继承自本仓库的 armstrong-number.md 技术文档,读者读完后既能透彻理解阿姆斯特朗数的判定原理,也能直接对照任意主流语言落地可运行的isArmstrong实现。

问题定义与数学背景

阿姆斯特朗数(又称水仙花数推广)的定义是:一个整数等于其每一位数字分别按"该数总位数"作为指数求幂后再求和的结果。形式化表达为:

设正整数nk位数字,且十进制表示为d₁d₂...dₖ,若d₁^k + d₂^k + ... + dₖ^k = n,则n是阿姆斯特朗数。

典型例子:

  • 153:3 位数,1³ + 5³ + 3³ = 1 + 125 + 27 = 153,成立。
  • 1634:4 位数,1⁴ + 6⁴ + 3⁴ + 4⁴ = 1 + 1296 + 81 + 256 = 1634,成立。

注意指数必须等于该数自身的位数,而不是某个固定常数(后文"常见陷阱"会专门强调这一点)。整个判定流程可以拆解为三步:统计位数 k → 逐位提取数字 → 累加 digit^k 并与原数比较。三种解法唯一的分歧点只在于"如何得到 k"。

本仓库将本题的完整讲解收录于 articles/armstrong-number.md,与 duplicate-integer.md 等文章同属articles/目录,遵循 articles/README.md 中"至少提供一种与 NeetCode 视频一致的多语言解法、给出时间与空间复杂度、尽量覆盖全部相关解法"的写作规范。仓库 README.md 中列出的 Python、Java、JavaScript、C++、Go、Swift、C#、TypeScript、Rust、Kotlin、Ruby、C、Scala、Dart 多语言体系,也正是本文三套方案所覆盖的代码生态。

前置知识(Prerequisites)

原文档要求读者在动手前掌握三项基础技能:

  • 数位提取(Digit Extraction):利用n % 10取出最低位、n // 10去掉最低位,反复迭代即可逐一拿到每一位数字,这是所有方案共用的核心操作。
  • 幂运算(Exponentiation):需要能够计算digit^k。各语言实现分别借助**(Python)、Math.pow(Java/Kotlin/C#)、pow(C++)、Math.pow(JavaScript)、pow(Rust)等内置能力,或用循环连乘自行实现。
  • 基础数学(Basic Math):理解数字的十进制性质与位数统计方法,包括"除以 10 缩位"的计数思想,以及"floor(log10(n)) + 1等于正整数的位数"这一对数恒等式。

方法一:通过字符串转换计算 k

直觉(Intuition)

最直观的做法是先把数字转成字符串:字符串的长度天然就是位数k,随后再按"取模取位、整除缩位"的方式逐个提取数字,累加digit^k。之所以先求k再求和,是因为 Armstrong 数的指数依赖于位数——例如153用 3 次方、1634用 4 次方,二者不能混用。

算法步骤

  1. n转为字符串并取其长度,得到k
  2. 循环执行n % 10提取最低位数字,随后n //= 10去掉该位,直到n变为 0。
  3. 对每一位数字累加digit^k到总和result
  4. result == n(比较时使用最初保存的原值),返回true,否则返回false

多语言实现

class Solution: def isArmstrong(self, n: int) -> bool: def getSumOfKthPowerOfDigits(num, k): result = 0 while num != 0: result += (num % 10) ** k num //= 10 return result length = len(str(n)) return getSumOfKthPowerOfDigits(n, length) == n
class Solution { public int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += Math.pow(n % 10, k); n /= 10; } return result; } public boolean isArmstrong(int n) { int length = String.valueOf(n).length(); return getSumOfKthPowerOfDigits(n, length) == n; } }
class Solution { public: int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += pow(n % 10, k); n /= 10; } return result; } bool isArmstrong(int n) { int length = to_string(n).length(); return getSumOfKthPowerOfDigits(n, length) == n; } };
class Solution { /** * @param {number} n * @return {boolean} */ isArmstrong(n) { const getSumOfKthPowerOfDigits = (num, k) => { let result = 0; while (num !== 0) { result += Math.pow(num % 10, k); num = Math.floor(num / 10); } return result; }; const length = String(n).length; return getSumOfKthPowerOfDigits(n, length) === n; } }
public class Solution { private int GetSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += (int)Math.Pow(n % 10, k); n /= 10; } return result; } public bool IsArmstrong(int n) { int length = n.ToString().Length; return GetSumOfKthPowerOfDigits(n, length) == n; } }
func isArmstrong(n int) bool { getSumOfKthPowerOfDigits := func(num, k int) int { result := 0 for num != 0 { digit := num % 10 power := 1 for i := 0; i < k; i++ { power *= digit } result += power num /= 10 } return result } length := len(fmt.Sprintf("%d", n)) return getSumOfKthPowerOfDigits(n, length) == n }
class Solution { private fun getSumOfKthPowerOfDigits(n: Int, k: Int): Int { var num = n var result = 0 while (num != 0) { result += Math.pow((num % 10).toDouble(), k.toDouble()).toInt() num /= 10 } return result } fun isArmstrong(n: Int): Boolean { val length = n.toString().length return getSumOfKthPowerOfDigits(n, length) == n } }
class Solution { func isArmstrong(_ n: Int) -> Bool { func getSumOfKthPowerOfDigits(_ num: Int, _ k: Int) -> Int { var num = num var result = 0 while num != 0 { var power = 1 for _ in 0..<k { power *= num % 10 } result += power num /= 10 } return result } let length = String(n).count return getSumOfKthPowerOfDigits(n, length) == n } }
impl Solution { pub fn is_armstrong(n: i32) -> bool { fn get_sum_of_kth_power_of_digits(mut num: i32, k: u32) -> i32 { let mut result = 0; while num != 0 { result += (num % 10).pow(k); num /= 10; } result } let length = n.to_string().len() as u32; get_sum_of_kth_power_of_digits(n, length) == n } }

复杂度分析

  • 时间复杂度:O(M),其中M是整数n的位数。字符串转换与逐位循环都只需要线性扫描一遍数字。
  • 空间复杂度:O(1),除极少量中间变量外不依赖额外空间(各语言内部字符串/格式化开销不计入算法空间)。

方法二:通过对数运算计算 k

直觉(Intuition)

不借助字符串,改用对数恒等式:正整数n的位数等于floor(log10(n)) + 1。例如log10(153) ≈ 2.184,向下取整得 2,再加 1 得到位数 3。这样做可以省去字符串分配的开销,在部分语言中更轻量。

算法步骤

  1. 计算k = floor(log10(n)) + 1得到位数。
  2. 用取模与整除循环提取每一位数字。
  3. 累加每一位的digit^k
  4. 若总和等于n则返回true

多语言实现

class Solution: def isArmstrong(self, n: int) -> bool: def getSumOfKthPowerOfDigits(num, k): result = 0 while num != 0: result += (num % 10) ** k num //= 10 return result length = int(math.log10(n)) + 1 return getSumOfKthPowerOfDigits(n, length) == n
class Solution { public int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += Math.pow(n % 10, k); n /= 10; } return result; } public boolean isArmstrong(int n) { int length = (int) Math.log10(n) + 1; return getSumOfKthPowerOfDigits(n, length) == n; } }
class Solution { public: int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += pow(n % 10, k); n /= 10; } return result; } bool isArmstrong(int n) { int length = log10(n) + 1; return getSumOfKthPowerOfDigits(n, length) == n; } };
class Solution { /** * @param {number} n * @return {boolean} */ isArmstrong(n) { const getSumOfKthPowerOfDigits = (num, k) => { let result = 0; while (num !== 0) { result += Math.pow(num % 10, k); num = Math.floor(num / 10); } return result; }; const length = Math.floor(Math.log10(n)) + 1; return getSumOfKthPowerOfDigits(n, length) === n; } }
public class Solution { private int GetSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += (int)Math.Pow(n % 10, k); n /= 10; } return result; } public bool IsArmstrong(int n) { int length = (int)Math.Log10(n) + 1; return GetSumOfKthPowerOfDigits(n, length) == n; } }
func isArmstrong(n int) bool { getSumOfKthPowerOfDigits := func(num, k int) int { result := 0 for num != 0 { digit := num % 10 power := 1 for i := 0; i < k; i++ { power *= digit } result += power num /= 10 } return result } length := int(math.Log10(float64(n))) + 1 return getSumOfKthPowerOfDigits(n, length) == n }
class Solution { private fun getSumOfKthPowerOfDigits(n: Int, k: Int): Int { var num = n var result = 0 while (num != 0) { result += Math.pow((num % 10).toDouble(), k.toDouble()).toInt() num /= 10 } return result } fun isArmstrong(n: Int): Boolean { val length = (Math.log10(n.toDouble()) + 1).toInt() return getSumOfKthPowerOfDigits(n, length) == n } }
class Solution { func isArmstrong(_ n: Int) -> Bool { func getSumOfKthPowerOfDigits(_ num: Int, _ k: Int) -> Int { var num = num var result = 0 while num != 0 { var power = 1 for _ in 0..<k { power *= num % 10 } result += power num /= 10 } return result } let length = Int(log10(Double(n))) + 1 return getSumOfKthPowerOfDigits(n, length) == n } }
impl Solution { pub fn is_armstrong(n: i32) -> bool { fn get_sum_of_kth_power_of_digits(mut num: i32, k: u32) -> i32 { let mut result = 0; while num != 0 { result += (num % 10).pow(k); num /= 10; } result } let length = (n as f64).log10() as u32 + 1; get_sum_of_kth_power_of_digits(n, length) == n } }

复杂度分析

  • 时间复杂度:O(M),其中M为位数。对数计算是 O(1) 的数学函数调用,真正耗时的是位数为M的逐位循环。
  • 空间复杂度:O(1),无需字符串缓冲区。

使用对数方案时需注意浮点精度:log10返回浮点数,必须向下取整后加 1。对本题目给定的正整数输入范围,该恒等式是精确的。

方法三:不使用任何内置方法计算 k

直觉(Intuition)

如果环境不允许调用字符串、对数等内置函数(例如某些嵌入式或面试限定场景),可以只用基础算术统计位数:反复把数字除以 10 并计数,直到数字变为 0,循环次数就是位数k。这一方案不依赖任何语言库,可移植性最强。

算法步骤

  1. n复制到临时变量tempN
  2. 循环tempN /= 10并计数length++,直到tempN为 0,得到位数k
  3. 从原始数n中逐位提取数字,累加digit^k
  4. 若总和等于n返回true

多语言实现

class Solution: def isArmstrong(self, n: int) -> bool: def getSumOfKthPowerOfDigits(num, k): result = 0 while num != 0: result += (num % 10) ** k num //= 10 return result length = 0 temp_n = n while temp_n != 0: length += 1 temp_n //= 10 return getSumOfKthPowerOfDigits(n, length) == n
class Solution { public int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while(n != 0) { result += Math.pow(n % 10, k); n /= 10; } return result; } public boolean isArmstrong(int n) { int length = 0; int tempN = n; while (tempN != 0) { length++; tempN /= 10; } return getSumOfKthPowerOfDigits(n, length) == n; } }
class Solution { public: int getSumOfKthPowerOfDigits(int n, int k) { int result = 0; while(n != 0) { result += pow(n % 10, k); n /= 10; } return result; } bool isArmstrong(int n) { int length = 0; int tempN = n; while (tempN) { length++; tempN /= 10; } return getSumOfKthPowerOfDigits(n, length) == n; } };
class Solution { /** * @param {number} n * @return {boolean} */ isArmstrong(n) { const getSumOfKthPowerOfDigits = (num, k) => { let result = 0; while (num !== 0) { result += Math.pow(num % 10, k); num = Math.floor(num / 10); } return result; }; let length = 0; let tempN = n; while (tempN !== 0) { length++; tempN = Math.floor(tempN / 10); } return getSumOfKthPowerOfDigits(n, length) === n; } }
public class Solution { private int GetSumOfKthPowerOfDigits(int n, int k) { int result = 0; while (n != 0) { result += (int)Math.Pow(n % 10, k); n /= 10; } return result; } public bool IsArmstrong(int n) { int length = 0; int tempN = n; while (tempN != 0) { length++; tempN /= 10; } return GetSumOfKthPowerOfDigits(n, length) == n; } }
func isArmstrong(n int) bool { getSumOfKthPowerOfDigits := func(num, k int) int { result := 0 for num != 0 { digit := num % 10 power := 1 for i := 0; i < k; i++ { power *= digit } result += power num /= 10 } return result } length := 0 tempN := n for tempN != 0 { length++ tempN /= 10 } return getSumOfKthPowerOfDigits(n, length) == n }
class Solution { private fun getSumOfKthPowerOfDigits(n: Int, k: Int): Int { var num = n var result = 0 while (num != 0) { result += Math.pow((num % 10).toDouble(), k.toDouble()).toInt() num /= 10 } return result } fun isArmstrong(n: Int): Boolean { var length = 0 var tempN = n while (tempN != 0) { length++ tempN /= 10 } return getSumOfKthPowerOfDigits(n, length) == n } }
class Solution { func isArmstrong(_ n: Int) -> Bool { func getSumOfKthPowerOfDigits(_ num: Int, _ k: Int) -> Int { var num = num var result = 0 while num != 0 { var power = 1 for _ in 0..<k { power *= num % 10 } result += power num /= 10 } return result } var length = 0 var tempN = n while tempN != 0 { length += 1 tempN /= 10 } return getSumOfKthPowerOfDigits(n, length) == n } }
impl Solution { pub fn is_armstrong(n: i32) -> bool { fn get_sum_of_kth_power_of_digits(mut num: i32, k: u32) -> i32 { let mut result = 0; while num != 0 { result += (num % 10).pow(k); num /= 10; } result } let mut length = 0u32; let mut temp_n = n; while temp_n != 0 { length += 1; temp_n /= 10; } get_sum_of_kth_power_of_digits(n, length) == n } }

复杂度分析

  • 时间复杂度:O(M),其中M为位数。统计位数的循环与求和循环各执行M次,总体仍为线性。
  • 空间复杂度:O(1),仅使用几个临时整型变量。

从源码结构看,Go 与 Swift 版本在求幂时没有依赖内置pow,而是用嵌套循环power *= digit连乘k次实现幂运算,因此这三种解法在所有语言中都不依赖pow/**之外的库函数,进一步印证了"纯算术可移植"的定位。

三种方案横向对比

方案求 k 的方式依赖的内置能力适用场景
方法一:字符串转换str(n)取长度字符串/格式化 API代码最直观,适合日常刷题快速实现
方法二:对数运算floor(log10(n)) + 1log10数学库无字符串分配,实现更紧凑
方法三:纯算术循环反复除以 10 计数无(仅基础算术)受限环境或追求最大可移植性

三种方案共享同一个判定核心——getSumOfKthPowerOfDigits(num, k)子过程,唯一区别仅在位数k的获取方式;三者时间复杂度均为O(M)、空间复杂度均为O(1)M为位数)。选择哪种方案取决于代码可读性偏好与实际运行环境约束。

常见陷阱(Common Pitfalls)

原文档专门列出两个高频踩坑点,这里逐一展开说明。

陷阱一:把指数写死为固定值

阿姆斯特朗数要求指数等于该数的实际位数。新手最容易把指数硬编码成 3(因为常接触水仙花数153),但这会直接导致错误:1634是 4 位数,必须使用 4 次方;固定用 3 次方会算出1+216+27+64 = 308 ≠ 1634

# Wrong: always using power 3 result += digit ** 3

正确做法是动态计算位数k后再用于幂运算,这也是三种解法都先求k的原因。

陷阱二:在提取数字时破坏了原数

逐位提取的循环会把n不断整除到 0。如果在进入循环前没有保存原始值,循环结束后n已经是 0,再也无法与求和结果比较,判定必然错误。

# Wrong: n is modified and can't be compared later while n != 0: result += (n % 10) ** k n //= 10 return result == n # n is now 0!

修复方式:要么像方法三那样先用tempN副本统计位数、再对原始n求和,要么在函数内部先把n复制给局部变量num,保证比较时仍有原始值可用。观察上文九种语言的实现可以发现,所有正确版本都遵守了这一约定(如 Go 版闭包参数num、Rust 版mut num均是对传入值的副本操作)。

总结

阿姆斯特朗数判定是一个"数学定义 → 数位分解 → 幂求和"的标准入门题,核心考点有二:一是理解指数与位数的动态绑定关系,二是在逐位循环中保护好原始值。本文基于 articles/armstrong-number.md 完整呈现了字符串、对数、纯算术三种求位数方案,并提供 Python、Java、C++、JavaScript、C#、Go、Kotlin、Swift、Rust 九个语言的对照实现。这套"同题多解 + 多语言对照"的组织方式与本仓库整体风格一致——仓库在 README.md 中即按 Arrays & Hashing、Two Pointers、Sliding Window 等分类维护数百道题的解法,每篇 articles 都要求给出复杂度分析与覆盖全部相关解法,方便读者跨语言对照、按需取用。

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

OSI七层模型解析与网络故障排查实战

1. OSI七层模型概述计算机网络领域有个经典的理论框架&#xff0c;就像建筑行业的施工蓝图一样重要——这就是OSI七层模型。1984年国际标准化组织&#xff08;ISO&#xff09;发布的这个参考模型&#xff0c;把复杂的网络通信过程分解成七个逻辑层&#xff0c;每层都有明确的职…

作者头像 李华
网站建设 2026/9/17 22:10:52

2023上半年软考数据库系统工程师上午真题复盘与备考指南

“数据库系统工程师”这个中级资格在软考里一直是报考大户&#xff0c;2023年上半年那场考试更是特别——它是软考机考改革前一场大规模的传统笔试&#xff0c;上午的《基础知识》科目仍然沿用75道单选题、150分钟、45分及格的老规矩。我在考场上最大的感受是&#xff1a;这张卷…

作者头像 李华
网站建设 2026/9/17 22:10:21

一条 TaoToken Key:补丁验证 Agent 在 FLAWED 里分档跑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华