真实面经题目 · 原创解析
如何用链表实现两个大数字相加?
链表大数相加要逐位处理两个链表和进位,核心是对齐位序、维护 carry,并在末尾补上最后的进位节点。
出现于:华为 · 后端开发
真实面经题目 · 原创解析
链表大数相加要逐位处理两个链表和进位,核心是对齐位序、维护 carry,并在末尾补上最后的进位节点。
如果链表按低位在前存储,如 2->4->3 表示 342,那么可以同时遍历两个链表,每轮取当前位值和 carry 相加,生成 sum % 10 的新节点,carry = Math.floor(sum / 10),直到两个链表和 carry 都为空。若链表按高位在前存储,则可以先反转链表,或用栈从低位弹出相加。面试时要讲清输入位序、链表长度不同、最后进位、是否允许修改原链表,以及时间 O(n)、空间 O(n) 或原地改造的取舍。
链表大数题最关键的澄清是低位在前还是高位在前。低位在前可以直接从头相加,高位在前通常要反转或借助栈,否则会从最高位开始无法处理进位。
同时移动两个链表指针,空节点按 0 处理。当前位相加再加 carry,新节点保存个位数,carry 保存十位进位,下一轮继续参与计算。
两个数字长度可能不同,短链表遍历完后仍要继续处理长链表剩余位。循环条件应是任一链表未结束或 carry 不为 0,不能只按较短链表停止。
例如 999 + 1 最后会留下 carry = 1,需要额外创建一个节点。漏掉这个节点是手写题最常见的边界错误,也会让结果少一位。
最稳妥的做法是创建新链表,避免修改输入。如果面试官要求节省空间,可以复用较长链表节点,但代码复杂度和副作用要说明清楚。
function addTwoNumbers(l1, l2) {
const dummy = { val: 0, next: null };
let tail = dummy;
let carry = 0;
while (l1 || l2 || carry) {
const left = l1 ? l1.val : 0;
const right = l2 ? l2.val : 0;
const sum = left + right + carry;
tail.next = { val: sum % 10, next: null };
tail = tail.next;
carry = Math.floor(sum / 10);
l1 = l1 ? l1.next : null;
l2 = l2 ? l2.next : null;
}
return dummy.next;
} 可以把两个链表的值压入栈,再从栈顶弹出低位相加,头插法构造结果链表。
可以把空链表视为 0,若两者都为空按约定返回空或单节点 0,面试中要说明返回约定。
遍历最长链表一次,时间 O(max(m,n));新建结果链表需要 O(max(m,n)) 空间。