题目:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
解法一(加法类型计算):
将l1和l2输入转换成整数类型(int\long long)的数,然后求和,再将结果用单链表的数据结构进行保存,如下为笔者代码:
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
unsigned long long result1 = 0;
unsigned long long result2 = 0;
unsigned long long result = 0;
unsigned long long l1_number = 1;
unsigned long long l2_number = 1;
ListNode* result_listNode = new ListNode();
ListNode* current_listNode = result_listNode;
while(l1!=nullptr){
result1 = result1 + (l1->val)*l1_number;
l1_number*=10;
l1 = l1->next;
}
while(l2!=nullptr){
result2 = result2 + (l2->val)*l2_number;
l2_number*=10;
l2 = l2->next;
}
result = result1+result2;
std::string str = std::to_string(result);
reverse(str.begin(), str.end());
int length = str.size();
for(int i =0;i<length;i++){
int a = str[i] - '0';
if(i==0){
current_listNode->val = a;
continue;
}
ListNode* new_ListNode = new ListNode(a);
current_listNode->next = new_ListNode;
current_listNode = current_listNode->next;
}
return result_listNode;
}
};
上述代码当l1或者l2长度很大时,会导致整数(int/ long long)超限,无法满足计算要求,因此这种方法仅用作理解,并不是解决本题的方法。
解法二(模拟):
由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加,我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。
如果当前两个链表处相应位置的数字为 n1,n2,进位值为 carry,则它们的和为 n1+n2+carry;其中,答案链表处相应位置的数字为 (n1+n2+carry)mod10,而新的进位值为 ⌊10n1+n2+carry⌋。如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 0 。此外,如果链表遍历结束后,有 carry>0,还需要在答案链表的后面附加一个节点,节点的值为 carry。如下代码:
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
//通过ListNode* head = nullptr创建初始空的链表,而不采用ListNode* head = new ListNode()的方式创建初始链表,这种方式是默认创建了值为0的链表节点且并不是创建了一个空的链表
ListNode *head = nullptr, *tail = nullptr;
int carry = 0;
while (l1 || l2) {
int n1 = l1 ? l1->val: 0;
int n2 = l2 ? l2->val: 0;
int sum = n1 + n2 + carry;
if (!head) {
head = tail = new ListNode(sum % 10);
} else {
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
carry = sum / 10;
if (l1) {
l1 = l1->next;
}
if (l2) {
l2 = l2->next;
}
}
if (carry > 0) {
tail->next = new ListNode(carry);
}
return head;
}
};
时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。我们要遍历两个链表的全部位置,而处理每个位置只需要 O(1) 的时间。空间复杂度:O(1)。注意返回值不计入空间复杂度。
笔者小记:
1、int n1 = l1 ? l1->val: 0;判断l1是否为空,若不为空(True)则 int n1 = l1->val,若为空(False)则 int n1 = 0;其中如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 0。进行补零操作。
2、当创建新的链表节点时,可以直接通过new ListNode()进行创建,例如head = tail = new ListNode(sum % 10);其中包括连续赋值操作以及直接通过new ListNode()创建新的链表节点,而不需要先创建指针类型的链表节点再对将其进行添加到现有链表中去。例如,ListNode* new_ListNode = new ListNode(a);current_listNode->next = new_ListNode;可以简化为current_listNode->next=new ListNode(a)。特别注意地是new关键字不仅是新增链表时创建并添加新的链表节点的方式,对二叉树这种需要动态添加节点的数据结构也是创建并添加新节点的方式。通过ListNode* head = nullptr创建初始空的链表,而不采用ListNode* head = new ListNode()的方式创建初始链表,这种方式是默认创建了值为0的链表节点且并不是创建了一个空的链表。