【中等】445-两数相加Ⅱ Add Two Numbers
题目重述
You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Follow up:
What if you cannot modify the input lists? In other words, reversing the lists is not allowed.
给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
Example
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7
来源:力扣(LeetCode)
链接:https://leetcode.com/problems/add-two-numbers-ii
解题过程
方法一:反转链表
解题思路
加法是从低位加起进位到高位,但是链表的方向是从高位到低位,不能直接从低位开始计算,所以,最直接的想法是首先将链表翻转过来,逐步相加得到低位->高位的链表,然过后再次反转从而得到我们期望的结果。
但是此时我们可以发现,从低位开始逐步相加的过程已经倒序得到了最终的链表,我们可以直接反向生成高位->低位的链表,而非对低位->高位的链表再次反转。
在程序编写过程中,最重要的仍旧是特殊情况分析。之所以加法一定要从低位开始做,是因为当两个数相加时结果可能大于9,这时候需要进位,所以我们需要一个中间变量用来记录每一位上的加法操作是否有进位产生;除此之外,由于两个链表长度也不一定是一致的,所以在逐位相加时要注意链表长度的限制。(详见Leetcode第二题 https://www.cnblogs.com/suata/p/12699711.html)
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverse(ListNode* l){ // 反转
ListNode* before = l;
ListNode* current = l->next;
before->next = NULL;
while(current != NULL){
ListNode* next = current->next;
current->next = before;
before = current;
current = next;
}
return before;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
l1 = reverse(l1);
l2 = reverse(l2);
ListNode* tail = new ListNode((l1->val + l2->val) % 10, NULL);
int add = (l1->val + l2->val) / 10;
while(l1->next != NULL or l2->next != NULL or add != 0){ // 只要有下一位或者进位就新创建节点
int temp = add;
if(l1->next){
temp += l1->next->val;
l1 = l1->next;
}
if(l2->next) {
temp += l2->next->val;
l2 = l2->next;
}
ListNode* curr = new ListNode(temp%10, tail); // 反向创建链表
tail = curr;
add = temp/10;
}
return tail;
}
};
方法二:栈
解题思路
与反向链表的分析过程相同,我们想要先从头开始向后找得到尾,然后再从尾开始操作得到头,也就是说先访问到的内容最后用到,这与栈先进后出得思想是一致的,所以,栈方法与反向链表方法是异曲同工的,都要先找到可以使得对应数据按顺序输出的数据结构,只是前者仍然是链表,后者用了栈而已。
代码
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int> s1, s2;
while (l1) {
s1.push(l1 -> val);
l1 = l1 -> next;
}
while (l2) {
s2.push(l2 -> val);
l2 = l2 -> next;
}
ListNode* tail = new ListNode(s1.top() + s2.top() % 10, NULL);
int add = (s1.top() + s2.top()) / 10;
s1.pop();
s2.pop();
while(!s1.empty() or !s2.empty() or add != 0){ // 只要有下一位或者进位就新创建节点
int temp = add;
if(!s1.empty()){
temp += s1.top();
s1.pop();
}
if(!s2.empty()){
temp += s2.top();
s2.pop();
}
ListNode* curr = new ListNode(temp%10, tail); // 反向创建链表
tail = curr;
add = temp/10;
}
return tail;
}
};
总结
-
创建链表不一定要从头到尾,还可以从尾到头
-
当出现顺序反转的场景时,就可以考虑栈是否可用了