2.两数相加
食用指南:
Leetcode专栏开启了,由于博主闭关期末,所以每日只能一题
尽量做到一题多解,先说思路,之后代码实现,会添加必要注释
语法或STL内容会在注意点中点出,新手友好
欢迎关注博主神机百炼专栏,内涵算法基础详细讲解和代码模板
题目描述:
-
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式(个位是链表头节点)存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
-
代码背景
/**
* Definition for singly-linked list.
* 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) {
}
};
- 题目来源:https://leetcode.cn/problems/add-two-numbers/
题目分析:
-
问题隐患:
最高位进位可能为1,可能为0,为0时不进位。
-
法一:从头节点开始存储位,多余尾节点需要删除
-
法二:从头节点下一节点存储位,return 头节点的下一节点
算法模板:
代码实现:
法一:删尾
- 删除尾节点需要将尾节点的前一节点的next指向NULL,防止内存泄露
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode();
auto p = head;
ListNode* q = NULL; //记录尾节点的上一节点
int t = 0;
while(l1 || l2){
if (l1) t += l1->val, l1 = l1->next;
if (l2) t += l2->val, l2 = l2->next;
p->val = t %10;
t /= 10;
p->next = new ListNode();
q = p;
p = p->next;
}
if (t) p->val = t;
else delete p, q->next = NULL;
return head;
}
};
错因1:内存泄露
-
若最终不将删除节点的上一节点的next指针指向NULL,
遍历链表时势必导致内存泄露
报错如下:==42==ERROR: AddressSanitizer: heap-use-after-free on address Ox6020000001b8 at pc Ox00000037afd4 bp 0x7fff22030230 sp 0x7fff22030228 READ of size 8 at Ox6020000001b8 thread TO
法二:空头
-
一定要保证节点和节点之间依靠next指针连接,
不然导致链表断开,直接造成答案不全,甚至内存泄漏
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
auto head = new ListNode();
auto p = head;
int t = 0;
while(l1 || l2 || t){
if (l1) t += l1->val, l1 = l1->next;
if (l2) t += l2->val, l2 = l2->next;
p->next = new ListNode(t % 10);//注意此处用next连接下一节点
t /= 10;
p = p->next;
}
return head->next;
}
};
错因2:链表断开
- 来看看链表没连接上的错误情况:
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
auto head = new ListNode();
auto p = head->next; //此处链表尚且连接
int t = 0;
while(l1 || l2 || t){
if (l1) t += l1->val, l1 = l1->next;
if (l2) t += l2->val, l2 = l2->next;
p = new ListNode(t % 10);
//此处p不再是next指向的空间,而是操作系统分配的ListNode空间
//链表节点断开
t /= 10;
p = p->next;
}
return head->next;
}
};
注意点:
1. 删除链表节点后造成的内存泄露:
- 我们学习delete关键字的时候,经常看见这句话:
delete指针指向空间后,应该将指针指向NULL
int *p = &a;
delete p;
p = NULL;
-
这句话本意是对的,但是对于链表删除尾节点,还需要加一句
delete 链表尾节点指针p后
将p指针和尾节点的上一节点的next指针都指向NULL
防止遍历链表时内存泄露 -
内存泄露体现在Leetcode上是:
==42==ERROR: AddressSanitizer -
体现在本地编译器上是:
疯狂打印地址
2. 构造链表未使用next导致节点不相连:
- 现手动构造一个ListNode *l1
- 先指向空间,再创建节点,导致链表不连接
ListNode* l1 = new ListNode(2);
ListNode* p = l1->next; //p指向l1的next
p = new ListNode(4); //p指向新创建的1号空间,不指向l1的next了
p = p->next; //p指向1号空间的next
p = new ListNode(3); //p指向新创建的2号空间,不指向一号空间的next了
- 先创建节点,再指向空间,链表还是连接的
ListNode* l1 = new ListNode(2);
ListNode* p = new ListNode(4);
l1->next = p;
ListNode* q = new ListNode(3);
p->next = q;
- 上述连接的代码还可以这样写:
ListNode *l1 = new ListNode(2);
ListNode *p = l1; //p指向1号节点
p->next = new ListNode(4);
p = p->next; //p指向2号节点
p ->next = new ListNode(3);
p = p->next; //p指向3号节点