力扣刷题-单链表两数相加

分区:链表

2.两数相加

题目描述:

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

(来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-two-numbers)

思路与算法

可以看出,两个输入的链表是逆序的,因此两个链表中同一位置的数字可以直接相加。

需要避坑的点:

1、如果两个链表的长度不一样,那对应位置的值该如何加减;

2、对应位置的进位值应该如何处理?我当时想的是判断这轮有没有进位,如果有的话就把链表L1的下一位值通过指针指向val来自加一,但后来发现这样的话当L1加到最后一位这种思路就没办法处理。

3、如何构建两数之和的链表?把对应位置相加之和的值赋给L1(L2也行)的对应位置?还是另外构建一个链表,每次得到对应位置的和之后构建新的结点,然后把该节点连接到新的结点上?答案显然是后者,因为按照前一种方案来的话,按照循环进行操作时,每轮执行的操作应该是相同的,但是L1的长度可能和L2不同,那样的话在L1长度不足的时候就需另外构建结点来保存对应位置的值,然后再接到L1后面,但是这样的操作显然在循环过程中无法格外进行。

解决方案

针对避坑点1:用两个变量n1和n2在每轮循环时分别存储对应位置的值,并且如果在这轮循环中L1为空,但是L2还有值的话,可以利用条件运算符(?:)来使n1为0,这样就可以保证每轮对应位置都有数相加。

int n1 = l1 ? l1->val: 0;
int n2 = l2 ? l2->val: 0;

对于避坑点2:可以用一个变量carry来保存每轮相加的进位值,初始时令carry为0,但每轮循环结束就保存了这轮的进位值,然后作用到下一轮的两数相加中。

//本轮循环
sum = n1+n2+carry;
//......其他操作
carry = sum/10; //用取余操作得到进位值并保存,然后作用于下一轮循环

 对于避坑点3:我想的是建立一个链表(带头结点)之后,每次都在循环内用尾插法(常用,最近在复习考研)建立两数值和的单链表,并在返回之前删除头结点,不然不能AC(Accept)。这样的话就需要一个尾指针r来保存最后的结点位置,会耗费空间,但这是我这个本人看了答案之后总结出对自己比较友好记忆的方法,只能这样理解了。而且力扣给的官方解答比较巧妙,相信在比赛的时候我是不会想到的。

ListNode* head=new ListNode(-1); //C++语言建立头结点
ListNode *temp=nullptr,*r=head;  //temp是临时结点,在循环中构建结点用的,r是尾指针,初始时指向头结点

//......

//将结点插入两数之和的聊表中的操作
temp = new ListNode(sum%10);
r->next=temp;
r=temp;

下面给出官方解答和我的本办法:

官方解答:

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        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;
    }
};

链接:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-leetcode-solution/(求生欲) 

我的笨办法:

/**
 * 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) {
        ListNode* head=new ListNode(-1);  //建立头结点,构建两数之和的链表
        ListNode *temp=nullptr,*r=head;   
        int carry = 0; 
        int n1,n2;
        int sum=0;
        while(l1||l2){       //循环对每轮位置的数相加
            n1=l1?l1->val:0;   //这种对应位置的处理很巧妙,很多情况都能用到
            n2=l2?l2->val:0;
            sum = n1+n2+carry;
            temp = new ListNode(sum%10);   //取模运算来实现加减进位,这种构建结点的语法见结构体定义
            //尾插法插入操作
            r->next=temp;
            r=temp;
            carry = sum/10;   //用carry保存每一轮的进位值,并作用到下一轮的计算中
            //l1指针和l2指针移动操作
            if(l1){
                l1=l1->next;
            }
            if(l2){
                l2=l2->next;
            }
        }
        //!!!务必不要忽视对最后carry值的保存!!!
        if(carry>0){
            r->next=new ListNode(carry);
        }
        head = head->next;   //将头指针指向第一个元素,不要头结点
        return head;
    }
};

 最后果然的成绩虽然AC了,但这时间空间损耗也说明了我这算法的不友好性。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值