【链表专题】字节真题:单链表相加
本问题对应课程链接:剑指offer刷题第三期
要求:
1、请打卡你的解题代码,如果能简单说一下思路则简单说一下思路。
2、给出你代码所用的时间复杂度和空间复杂度
代码参考
有同学希望我找个代码给大家参考,我从打卡区里选了 @猫猫真可爱 同学的代码给大家参考哦,并且做了一些注释。
// 注意,关于链表节点的定义(ListNode),我这里没有给出来哦
class SolutionAdd {
// 反转链表
public ListNode reverseList(ListNode node){
ListNode pre = null;
ListNode q= null;
while (node != null) {
q= node.next;
node.next = pre;
pre = node;
node = q;
}
return pre;
}
// 单链表相加
public ListNode addTwoNumbers(ListNode head1, ListNode head2) {
// 虚拟头节点
ListNode listNode = new ListNode(0);
ListNode p = listNode;
// sum 用来存放进位
int sum = 0;
while (head1 != null || head2 != null || sum!=0) {
if (head1 != null) {
sum += head1.val;
head1 = head1.next;
}
if (head2 != null) {
sum += head2.val;
head2 = head2.next;
}
p.next = new ListNode(sum % 10);
sum = sum / 10;
p = p.next;
}
return listNode.next;
}
}
时间复杂度:O(n+m)
空间复杂度:O(1)
共以下 57 个回答
思路先反转再相加
/**
* 反转链表 – 递归实现
* @param head
* @return
*/
public LinkNode reverseList(LinkNode head){
if(head null || head.next null){
return head;
}
LinkNode tmp = reverseList(head.next);
head.next.next = head;
head.next = null;
return tmp;
}
/**
* 单链表相加-不允许使用栈,队列
* 先将两链表反转后按位相加,然后再反转
* @param headA
* @param headB
* @return
*/
思路:反转链表
时间复杂度:O(n + m)
空间复杂度:O(1)
“`java
class SolutionAdd(
//反转链表
public ListNode reverseList(ListNode node){
ListNode pre = null;
ListNode q = null;
while(node!=null){
q= node.next;
node.next=pre;
pre=node;
node=q;
}
return pre;
//单链表相加
public ListNode addTwoNumbers(ListNode heard1,ListNode head2){
//虚拟头节点
ListNode listNode = new ListNode(0);
ListNode p = listNode;
//sum用来存放进位
int sum = 0;
while(head1! = null||head2!=null|| sum!=0){
if(head1!=null){
sum+=head1.val;
head1=head1.next;
}
if(head2!=null){
sum+=head2.val;
head2=head2.next;
}
p.next= new ListNode(sum%10);
sum=sum/10;
p=p.next;
}
return listNode.next;
}
}
思路:反转链表+两数相加
“`cpp 运用翻转链表,先翻转,再相加,用temp记录进位 改变cur节点的val和next,最后翻转
class Node {
public:
int val;
Node* next;
};
class Solution {
public:
Node* reverseList(Node* head){
//reverse
Node *prev, *next = NULL;
Node *cur = head;
while(cur != nullptr){
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
head = prev;
return head;
}
};
“`
写了一个辅助函数对input链表进行反转,而后从最后一位开始进位相加,设置一bool变量标记进位
先进行链表得反转,然后从反转后得头节点开始依次进行+的操作,若大于十则通过一个变量add进行进位的储存,最后要注意的就是,计算到结尾的时候,add可能不为0,此时就要再创建一个节点来储存add的值
时间复杂度O(n+m), 空间复杂度O(1)
反转 相加 反转
代码:
时间:遍历两个链表各两次,O(A+B)
空间:O(1)
先对两链表遍历反转,再遍历加和
Time: O(N+M)
Space:O(1)
这道题与牛客网上的 链表相加(二)一样,觉得不好测试的同学可以在上面写,链接放在这里
https://www.nowcoder.com/practice/c56f6c70fb3f4849bc56e33ff2a50b6b
另外这道题是正向存放的,如果这道题是反向存放呢?题目链接也放在这里了,有兴趣的同学可以尝试一下 https://www.nowcoder.com/practice/521d83306d964c1188639033eb59621d?tpId=196&tqId=40273&ru=/exam/oj
如果想在力扣上做,这里也有一道类似的:面试题 02.05. 链表求和,但是这道题是反向链表相加
https://leetcode.cn/problems/sum-lists-lcci/
时间复杂度:O(n+m) 空间复杂度:O(1)
c++实现,先反转然后再相加再反转
ListNode* reverseList(ListNode* list)
{
if(listNULL&&list->nextNULL)return list;
ListNode* pre = NULL;
ListNode* cur = list;
ListNode* temp;
while(cur)
{
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ll1 = reverseList(l1);
ListNode* ll2 = reverseList(l2);
int carry = 0;
ListNode* asc=NULL;
while(ll1!=NULL||ll2!=NULL||carry!=0)
{
int a = ll1NULL?0:ll1->val;
int b = ll2NULL?0:ll2->val;
if(ll1!=NULL)ll1 = ll1->next;
if(ll2!=NULL)ll2 = ll2->next;
int cur = a+b+carry;
carry = cur/10;
cur %= 10;
auto curNode = new ListNode(cur);
curNode->next = asc;
asc = curNode;
}
return asc;
}
c++实现,先反转然后再相加再反转
ListNode* reverseList(ListNode* list)
{
if(listNULL&&list->nextNULL)return list;
ListNode* pre = NULL;
ListNode* cur = list;
ListNode* temp;
while(cur)
{
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* ll1 = reverseList(l1);
ListNode* ll2 = reverseList(l2);
int carry = 0;
ListNode* asc=NULL;
while(ll1!=NULL||ll2!=NULL||carry!=0)
{
int a = ll1NULL?0:ll1->val;
int b = ll2NULL?0:ll2->val;
if(ll1!=NULL)ll1 = ll1->next;
if(ll2!=NULL)ll2 = ll2->next;
int cur = a+b+carry;
carry = cur/10;
cur %= 10;
auto curNode = new ListNode(cur);
curNode->next = asc;
asc = curNode;
}
return asc;
}
时间复杂度:O(A+B)。
空间复杂度:O(1)。
打卡
class AddTwoLists {
public:
// 反转链表
ListNode* reverseList(ListNode* node) {
ListNode* pre = nullptr;
ListNode* temp = nullptr;
while (node != nullptr) {
temp = node->next;
node->next = pre;
pre = node;
node = temp;
}
return pre;
}
ListNode* TwoListsSum(ListNode* list1, ListNode* list2){
// 虚拟头结点
ListNode* dummy = new ListNode(-1);
ListNode* curr = dummy;
// sum 用来求和
int sum = 0;
// curry 用来存进位
int carry = 0;
while (list1 || list2 || carry) {
int sum = (list1 ? list1->val : 0) + (list2 ? list2->val : 0) + carry; // 计算当前位置的和
carry = sum / 10; // 计算进位值
ListNode* newNode = new ListNode(sum % 10); // 创建新节点
curr->next = newNode; // 将新节点连接到结果链表的末尾
curr = curr->next; // 更新当前节点
};
时间复杂度O(m+n),空间复杂度O(1)
思路:找出两个链表相差数以及最长链表,将最长链表进位到和短链表一样的长度,两两相加后覆盖原链表值
时间复杂度O(n+m) 空间复杂度O(1)
时间复杂度O(n),空间复杂度O(n)
可以
感觉写的太长了,反转的就不贴了,常规思路,但是把个位和进位分开,进位可单独设一个节点,这样更简单高效。
先反转链表,然后从个位数开始相加
时间复杂度:O(n) 空间复杂度:O(1)
很清晰的思路,就是13行是不是得ListNode*?
“`C++
class Solution {
public:
};
“`
思路
解题方法
复杂度
Code
“`Java []
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//判断条件注意有可能最终的List长度大于Max(l1,l2)的情况
while(reverL1 != null || reverL2 !=null || add!=0){
int num1 = reverL1!=null? reverL1.val: 0;
int num2 = reverL2!=null? reverL2.val: 0;
int sum = num1 + num2 + add;
add = sum/10;
prv= new ListNode(sum%10,prv);
reverL1 = reverL1 != null ? reverL1.next : null;
reverL2 = reverL2 != null ? reverL2.next : null;
}
return prv;
}
//先反转链表
}
“`
只用单链表有点繁琐
c实现
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
int sa[1024],topa=0,sb[1024],topb=0,s[1024],top=0;
int dec=0;
for(;l1;l1=l1->next)
{
sa[topa++]=l1->val;
}
for(;l2;l2=l2->next)
{
sb[topb++]=l2->val;
}
for(;topa||topb;top++)
{
if(topa)dec+=sa[–topa];
if(topb)dec+=sb[–topb];
s[top]=dec%10;
dec/=10;
}
if(dec>0||top0)
{
s[top++]=dec;
}
struct ListNode *dy=malloc(sizeof(struct ListNode)), *p;
p=dy;
for(;–top!=-1;)
{
p->next=malloc(sizeof(struct ListNode));
p=p->next;
p->val=s[top];
p->next=NULL;
}
return dy->next;
}
c实现
反转求和,再反转;
提交了5次,才通过
注意:边界情况
[9,9] [9]
[0] [3,7]
C++实现:
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
//反转链表
ListNode* pre = NULL;
ListNode* cur = head;
ListNode* tmp;
while(cur)
{
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
if(l1 nullptr) return l2;
else if(l2 nullptr) return l1;
ListNode* newl1 = reverseList(l1);
ListNode* newl2 = reverseList(l2);
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
int carry = 0;
while(newl1 != NULL || newl2 != NULL)
{
int val1 = newl1 NULL ? 0 : newl1->val;
int val2 = newl2 NULL ? 0 : newl2->val;
int sum = val1 + val2 + carry;
carry = sum / 10;
cur->next = new ListNode(sum % 10);
cur = cur->next;
if(newl1)
newl1 = newl1->next;
if(newl2)
newl2 = newl2->next;
}
//处理最后一个进位
if(carry 1)
{
cur->next = new ListNode(1);
}
return reverseList(dummy->next);
}
};
按照思路
C++
先翻转两个链表,然后相加,然后再翻转相加后的链表,返回结果
TC: O(n), n为两个链表的长度和
SC: O(1)
帮忙完善了下面peach同学的代码,感谢打卡分享,理清了这道题的逻辑
本地IDE
思路
先反转,每个对应节点元素相加,算进位,最后在反转输出接表
时间复杂度:O(n)
空间复杂度:O(1)
C++实现单链表相加
java实现
思路:原地反转即可。
时间复杂度为n,空间复杂度为1
链表反转+同时操作两个链表(加法模拟)
思路:先逆转两条链表,从末尾(个位)相加,考虑进位,不断增加新节点接入新的头节点。最终再次逆转该新链表。
时间复杂度O(m+n),空间复杂度因为开了一条新链存储结果,应该是O(max(m, n))吧
我在leetcode上见过这道题《链表中的两数相加》
先写一个反转链表函数,然后创建一个新链表,使用头插法保存每位的结果。最后如果还有进位,需要在前面再头插一个1
在本地测了几个是对的,写了很久。。。
public class AddList {
public static void main(String[] args) {
ListNode a=new ListNode(7);
a.next=new ListNode(3);
ListNode b=new ListNode(0);
ListNode node = addList(a, b);
System.out.println(node.val);
System.out.println(node.next.val);
//System.out.println(node.next.next.val);
}
}
}
思路:先各自反转,逐个节点相加,注意进位,将结果反转
时间复杂度:O(n)
额外空间复杂度:O(1)
链表反转之后,按位求和,同时需要设置一个进位标志,计算完成后,再进行反转得到结果
思路
代码
“`cpp
// 用了反转链表
// 特别注意最后一位为10 的情况
// 如果用栈的话,更好处理一点,但是需要的二外空间大
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* newl1 = reverlist(l1);
ListNode* newl2 = reverlist(l2);
ListNode* ans = new ListNode(0);
ListNode* dummy = ans;
int j=0,d=0;
while(newl1 && newl2){
int sum = newl1->val + newl2->val+j;
d = sum%10;
ans->next = new ListNode(d);
j = sum/10;
ans = ans->next;
newl1 = newl1->next;
newl2 = newl2->next;
}
while(newl1){
newl1->val += j;
d = newl1->val %10;
ans->next = new ListNode(d);
j = newl1->val / 10;
ans = ans->next;
newl1 = newl1->next;
}
while(newl2){
newl2->val += j;
d = newl2->val %10;
ans->next = new ListNode(d);
j = newl2->val / 10;
ans = ans->next;
newl2 = newl2->next;
}
ans = reverlist(dummy->next);
if(j!=0){
ListNode* temp = new ListNode(j);
temp->next = ans;
ans = temp;
}
return ans;
}
ListNode* reverlist(ListNode* head){
if(!head || !head->next){
return head;
}
ListNode* newhead = reverlist(head->next);
head->next->next = head;
head->next = nullptr;
return newhead;
}
};
反转之后考虑两个链表长度相同,进位+1时会空
参考代码: