跳到主要内容

字符串相加

问题

给定两个以字符串形式表示的非负整数 num1num2,返回它们的和,也以字符串形式表示。不能使用任何内建大数库或直接转换为整数

示例:

输入:num1 = "456", num2 = "77"
输出:"533"

来源:LeetCode 415. 字符串相加

为什么需要大数运算?

JavaScript 的 Number 类型最大安全整数是 25312^{53} - 1(约 9 × 10^15),超过就会丢失精度。前端处理订单号、身份证号等长数字时经常遇到。

答案

方法一:模拟竖式加法(推荐)

从末位开始逐位相加,处理进位:

addStrings.ts
function addStrings(num1: string, num2: string): string {
let i = num1.length - 1;
let j = num2.length - 1;
let carry = 0;
const result: string[] = [];

while (i >= 0 || j >= 0 || carry > 0) {
const n1 = i >= 0 ? num1.charCodeAt(i) - 48 : 0; // '0' 的 ASCII = 48
const n2 = j >= 0 ? num2.charCodeAt(j) - 48 : 0;

const sum = n1 + n2 + carry;
result.push(String(sum % 10)); // 当前位
carry = Math.floor(sum / 10); // 进位

i--;
j--;
}

return result.reverse().join('');
}

图解

    4 5 6
+ 7 7
─────────
carry: 0

6 + 7 + 0 = 13 → 写 3, carry = 1
5 + 7 + 1 = 13 → 写 3, carry = 1
4 + 0 + 1 = 5 → 写 5, carry = 0

result = ['3','3','5'] → reverse → "533"
复杂度分析
  • 时间复杂度O(max(m,n))O(\max(m, n)) — m、n 为两个字符串长度
  • 空间复杂度O(max(m,n))O(\max(m, n)) — 存储结果

方法二:使用数组下标(另一种写法)

addStringsAlt.ts
function addStrings(num1: string, num2: string): string {
// 确保 num1 是较长的
if (num1.length < num2.length) [num1, num2] = [num2, num1];

const arr1 = num1.split('').map(Number);
const arr2 = num2.split('').map(Number);

// 对齐:在 arr2 前面补零
while (arr2.length < arr1.length) arr2.unshift(0);

let carry = 0;
for (let i = arr1.length - 1; i >= 0; i--) {
const sum = arr1[i] + arr2[i] + carry;
arr1[i] = sum % 10;
carry = Math.floor(sum / 10);
}

if (carry > 0) arr1.unshift(carry);

return arr1.join('');
}

常见面试追问

Q1: 如何实现字符串相乘?(LeetCode 43)

答案:模拟竖式乘法,num1[i] * num2[j] 的结果累加到 result[i+j]result[i+j+1] 位置:

function multiply(num1: string, num2: string): string {
if (num1 === '0' || num2 === '0') return '0';

const m = num1.length, n = num2.length;
const result = new Array(m + n).fill(0);

for (let i = m - 1; i >= 0; i--) {
for (let j = n - 1; j >= 0; j--) {
const mul = (num1.charCodeAt(i) - 48) * (num2.charCodeAt(j) - 48);
const p1 = i + j; // 高位
const p2 = i + j + 1; // 低位
const sum = mul + result[p2];

result[p2] = sum % 10;
result[p1] += Math.floor(sum / 10);
}
}

// 去掉前导零
let start = 0;
while (start < result.length - 1 && result[start] === 0) start++;

return result.slice(start).join('');
}

Q2: BigInt 和手写大数的区别?

答案

特性BigInt (原生)手写大数
性能引擎优化,更快纯 JS 实现,较慢
兼容性IE 不支持全兼容
面试了解即可需要掌握原理
序列化JSON 不支持天然是字符串
// BigInt 用法
const a = BigInt('123456789012345678901234567890');
const b = 123n; // 字面量
const sum = a + b; // BigInt 只能和 BigInt 运算

Q3: 前端大数场景有哪些?

答案

  • 后端返回 Long 型 ID:JSON 中的数字超过安全整数(如雪花 ID),需要后端以字符串返回
  • 金融计算:金额计算需要精度
  • 身份证号/手机号:虽然是数字但应作为字符串处理
// 常见问题:JSON.parse 精度丢失
JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 } ← 精度丢失了!

// 解决方案:正则替换后再用 BigInt 处理
const jsonStr = '{"id": 9007199254740993}';
const fixed = jsonStr.replace(/:\s*(\d{16,})/g, ': "$1"');
JSON.parse(fixed); // { id: "9007199254740993" }

相关链接