题目来源
题目描述
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
}
};
题目解析
分析数据量
- 很小,说明可以随便造
不推荐的写法:
- 将两个链表先算出各自代表的整数,然后求出两个整数的和,最后将这个和转换为链表的形式。
- 为什么不推荐?因为链表的长度可以很长,因此可能会溢出。
难点在于:
- 我们进行加法运算都是从低位到高位计算
- 然后链表的最低位在链表末尾,链表只能从前往后遍历,没法取到前面的元素
怎么办呢?
使用栈
利用栈后入先出的特性逆序访问链表
- 将两个链表分别从左到右遍历,遍历过程中将值压栈,这样就生成了两个链表节点的逆序栈,分别表示stackA、stackB
- 将stackA和stackB同步弹出,这样就相当于两个链表从低位到高位依次弹出,在这个过程中生成相加链表即可,同时需要关注【每一位是否有进位】,用
c
表示 - 返回栈的头节点
#include <stack>
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
std::stack<int> stackA, stackB;
ListNode *iter = l1;
while (iter){
stackA.push(iter->val);
iter = iter->next;
}
iter = l2;
while (iter){
stackB.push(iter->val);
iter = iter->next;
}
ListNode * dummy = new ListNode(-1), *newNode;
int c = 0, y = 0;
while (!stackA.empty() or !stackB.empty() or c > 0){
int a = stackA.empty() ? 0 : stackA.top();
int b = stackB.empty() ? 0 : stackB.top();
if(!stackA.empty()){
stackA.pop();
}
if(!stackB.empty()){
stackB.pop();
}
y = (a + b + c) % 10;
c = (a + b + c) / 10;
newNode = new ListNode(y);
newNode->next = dummy->next;
dummy->next = newNode;
}
return dummy->next;
}
反转链表
利用链表的逆序,这样就可以省掉栈的空间。
- 将两个链表逆序,这样就可以依次得到从低位到高位的数字
- 同步遍历两个逆序后的链表,这样就依次得到两个链表从低位到高位的数字,在这个过程中生成相加链表即可,同时需要关注【进位】
- 将逆序的链表再逆序一次,返回结果
class Solution {
ListNode* reverseList(ListNode* node){
if(node == nullptr || node->next == nullptr){
return node;
}
ListNode *ret = reverseList(node->next);
node->next->next = node;
node->next = nullptr;
return ret;
}
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
l1 = reverseList(l1);
l2 = reverseList(l2);
ListNode *dummy = new ListNode(-1);
ListNode *l = dummy;
int c = 0;
while (l1 != nullptr || l2 != nullptr || c != 0){
int a = l1 != nullptr ? l1->val : 0;
int b = l2 != nullptr ? l2->val : 0;
l->next = new ListNode((a + b + c) % 10);
c = (a + b + c) / 10;
l1 = l1 != nullptr ? l1->next : nullptr;
l2 = l2 != nullptr ? l2->next : nullptr;
l = l->next;
}
auto ret = reverseList(dummy->next);
delete dummy;
return ret;
}
};
优化:插入新链表时可以采用头插法,这样就不需要反转链表了
递归
为什么可以用递归?
- 递归本质是栈,所以也可以从后往前取数字
测试
int main()
{
ListNode* listA = new ListNode(-1), *moveA = listA;
ListNode* listB = new ListNode(-1), *moveB = listB;
std::vector<int> vectorA = {7, 2, 4, 3};
for(auto i : vectorA){
moveA->next = new ListNode(i);
moveA = moveA->next;
}
vectorA = {5, 6, 4};
for(auto i : vectorA){
moveB->next = new ListNode(i);
moveB = moveB->next;
}
addTwoNumbers(listA->next, listB->next);
return 0;
}
类似题目
加法运算
- leetcode:2.逆序链表两数相加 Add Two Numbers
- leetcode:445. 正序链表两数相加 II Add Two Numbers II
- leetcode:67. 字符串形式的二进制相加 Add Binary
- leetcode:415. 字符串形式的数字相加 Add Strings
- leetcode:369. 逆序链表加一运算Plus One Linked List
- leetcode:66. 数组形式加一运算 Plus One
- leetcode:989. 数组形式的整数相加Add to Array-Form of Integer
乘法运算