
该函数虽使用固定长度的映射表,但核心 while 循环的执行次数与输入数值 num 成正比,因此时间复杂度为 o(n),其中 n 表示输入数值本身(而非字符串或数组长度)。
该函数虽使用固定长度的映射表,但核心 while 循环的执行次数与输入数值 num 成正比,因此时间复杂度为 o(n),其中 n 表示输入数值本身(而非字符串或数组长度)。
在算法分析中,“输入规模”(input size)是决定时间复杂度的关键基准。虽然 romanNumerals 数组长度恒为 12(常数),但函数的实际运行时间主要取决于 num 的大小——因为 while 循环会反复减去最大可能的罗马数值单位,其迭代总次数等于构造最终罗马字符串所需的字符单元总数。
以 num = 2023 为例:
- 2023 ≥ 1000 → 添加 "M",num 变为 1023(1 次)
- 1023 ≥ 1000 → 再加 "M",num 变为 23(2 次)
- 跳过 900、500… 直到 23 ≥ 10 → 加 "X",num=13;再加 "X",num=3;再加 "I"×3 → 共 7 次循环
可见:总循环次数 ≈ num 在贪心策略下被分解为若干罗马单位的次数,而每个单位至少为 1,因此最坏情况下(如 num = 3999,全由 "I" 构成)需执行约 3999 次加法与减法操作。尽管 num 被约束在 [1, 3999] 区间内,Big O 分析关注的是输入增长趋势下的渐近行为,而非实际工程中的上界截断。只要运算步数随 num 线性增长(即存在常数 c,使得 steps ≤ c × num),就定义为 O(num),习惯记作 O(n),其中 n 即输入数值本身。
值得注意的是:此处的 n 并非数组长度或字符串长度,而是数值型输入的大小——这属于“数值输入”的典型分析场景(类似判断质数、计算阶乘等)。若将输入按二进制位数衡量(即输入规模为 log₂(num)),则该算法实际为 O(2^m)(指数级),但常规实践中,对这类范围明确的整数输入,仍以数值本身为尺度更符合直觉与用途。
✅ 正确结论:
- 时间复杂度为 O(n),n 是输入整数 num 的值;
- 空间复杂度为 O(1)(忽略输出字符串),因辅助数组长度固定,变量数量恒定。
// 示例:对比不同输入的循环计数(可调试验证)
function convertRomanNumeralsWithCount(num) {
const romanNumerals = [
[1000,"M"],[900,"CM"],[500,"D"],[400,"CD"],
[100,"C"],[90,"XC"],[50,"L"],[40,"XL"],
[10,"X"],[9,"IX"],[5,"V"],[4,"IV"],[1,"I"]
];
let result = "", count = 0;
for (let i = 0; i = romanNumerals[i][0]) {
result += romanNumerals[i][1];
num -= romanNumerals[i][0];
count++; // 统计 while 总执行次数
}
}
console.log(`num=${num} → loops=${count}`);
return result;
}
// convertRomanNumeralsWithCount(10); // loops=1 ("X")
// convertRomanNumeralsWithCount(3999); // loops≈15 (MMMCMXCIX: M×3 + CM×1 + XC×1 + IX×1 = 3+2+2+2=9? 实际需逐位拆解,但总量线性增长)
⚠️ 注意事项:
- 不要混淆“固定表长”与“固定运行时间”——外层 for 循环是 O(1),但内层 while 的总开销主导整体复杂度;
- Big O 描述的是增长关系,不是绝对耗时;即使 num ≤ 3999,只要步数 ∝ num,就是 O(n);
- 若题目明确要求以“输入的二进制位数 b = ⌊log₂n⌋+1”为规模,则复杂度为 O(2^b),属伪多项式时间(pseudopolynomial),但本题语境下采用数值尺度更合理。
综上,理解时间复杂度的核心在于识别真正驱动运算量增长的输入维度——此处是 num 的大小,而非常量配置表的长度。











