文章内容是自己刷leetcode官方刷题攻略的一些经验与总结。
题目链接详见 leetcode刷题攻略
如果喜欢的话,别忘了点个赞哦 ^ _ ^
一. 2 两数相加
1.题目描述
2.题目分析与解答
题目中给的两个链表就相当于是两个加数,而且是逆序存储,也就是说,链表从前往后遍历,就正好符合加数从低位往高位开始加的运算逻辑。
我们同时遍历两个链表将相应位置的 value 相加即可,在这个过程中,我们需要记录一个进位位,初始化为 0。根据进位位的值以及两个 value 来确定当前位置的和是多少。
两个链表长度可能不同,我们在遍历过程中,如果发现有一条链表遍历完了,那么可以假设该链表的当前节点的值是一个 0,直到两个链表都遍历结束。
最后如果进位位不为 0,我们需要在答案的最后增加一个节点。
C++ 代码如下:
/**
* 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) {
//设置一个进位位
//逆序存储相当于就是从低位开始相加,符合正常的运算逻辑
//我们不用计算具体数字,只用找到相加后的链表即可
//两个链表长度可能不同
//注意最后处理进位
int carry = 0;
ListNode *head = nullptr, *tail = nullptr;
while(l1 || l2) {
int n1 = l1 ? l1->val : 0;
int n2 = l2 ? l2->val : 0;
int sum = n1 + n2 + carry;
carry = sum / 10;
if(head == nullptr) {
head = tail = new ListNode(sum % 10, nullptr);
} else {
tail->next = new ListNode(sum % 10, nullptr);
tail = tail->next;
}
if(l1) l1 = l1->next;
if(l2) l2 = l2->next;
}
if(carry > 0) {
tail->next = new ListNode(carry, nullptr);
}
return head;
}
};
二. 445 两数相加II
1.题目描述
2.反转链表解法
这道题目与上道题目的区别是链表的起始位置为数字的高位,我们不能一边遍历一边运算(应该从低位开始算)。
这里有两种解题思路。
第一种是将原始链表进行翻转,然后步骤就和 2.两数相加 这道题一样了,注意要将最后得到的链表再次进行翻转才是答案需要的链表。
时间复杂度 O(max(m,n)), 空间复杂度 O(1)。
C++ 代码如下:
/**
* 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) {
//如果将链表进行翻转那么就和两数相加这道题一样了
//注意最后输出进行翻转
//反转链表
l1 = Reverse(l1);
l2 = Reverse(l2);
//测试反转链表函数
// ListNode *cur = l1;
// while(cur) {
// cout << cur->val << " ";
// cur = cur->next;
// }
// cout << endl;
// cur = l2;
// while(cur) {
// cout << cur->val << " ";
// cur = cur->next;
// }
// cout << endl;
//两数相加
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;
carry = sum / 10;
if(head == nullptr) {
head = tail = new ListNode(sum % 10, nullptr);
} else {
tail->next = new ListNode(sum % 10, nullptr);
tail = tail->next;
}
if(l1) l1 = l1->next;
if(l2) l2 = l2->next;
}
//处理进位
if(carry) {
tail->next = new ListNode(1, nullptr);
}
//注意最后的输出也要翻转回来
return Reverse(head);
}
ListNode* Reverse(ListNode *head) {
ListNode *cur = head;
ListNode *pre = nullptr;
while(cur) {
ListNode *tem = cur->next;
cur->next = pre;
pre = cur;
cur = tem;
}
return pre;
}
};
3.使用栈求解
第二种解法是使用栈这种数据结构,利用它先进后出的性质。
我们将两个链表的数值放到两个栈中,在遍历两个栈,那么此时栈顶元素就是对应加数的低位,后面的过程就和两数相加这道题目类似了。
时间复杂度 O(max(m,n)), 空间复杂度 O(m+n)。
C++ 代码如下:
/**
* 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) {
//如果将链表进行翻转那么就和两数相加这道题一样了
//注意最后输出进行翻转
//时间复杂度O(max(m,n)) 空间复杂度O(1)
//如果不翻转链表的话,可以用栈(不能叫优化),正好满足先进后出的运算顺序
//时间复杂度O(max(m,n)) 空间复杂度O(m+n)
stack<int> s1, s2;
//入栈
while(l1) {
s1.push(l1->val);
l1 = l1->next;
}
while(l2) {
s2.push(l2->val);
l2 = l2->next;
}
int carry = 0;
ListNode *head = nullptr;
while(!s1.empty() || !s2.empty() || carry) {
int n1 = s1.empty() ? 0 : s1.top();
int n2 = s2.empty() ? 0 : s2.top();
if(!s1.empty()) s1.pop();
if(!s2.empty()) s2.pop();
int sum = n1 + n2 + carry;
carry = sum / 10;
ListNode * tem = new ListNode(sum % 10, head);
head = tem;
}
return head;
}
};