LeetCode算法问题1 —— Add Two Numbers

本文解析LeetCode上的加法链表问题,探讨反向存储的优势及不同长度链表的处理方法,给出简洁高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在学校提供网站LeetCode上刷算法题,近段时间会将一些自己觉得有意义,值得效仿的点写下来,为自己以后的工作和学习所用。

今天讲的是Add Two Numbers

首先看一下问题描述
这里写图片描述
问题大概意思是这样的:分别将两个非负整数的各个数位用链表连接起来,且数字的存储方向是反向的,即,2->4->3其实是342。要求我们计算出两个整数的和并以相同方式返回。

同时,题目提供了这种链式的数据结构

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

刚读问题的时候我就在想:为什么要把数反向存储?后来拿了几个数试验了一下发现了这个的好处。以2->0->7(702)、8->0->8(808)为例,自然702+808=1510,按照我们正常的思维来顺序存储的话,那么运算必须从右往左,这是因为我们在没有计算低位的时候永远不知道是否会向高位进位,这就意味着我们必须先把指针移到末尾,然后再去逐个找父节点,这是ListNode这个数据结构无法实现的,同时也做了很多无用功(从开头将最高位指针移到末尾最低位指针)。

而反向存储完美地规避了这些问题,它本身初始节点就是最低位,这是我觉得这个问题设计得比较巧妙的点睛之笔,只是调换了个顺序,问题难度瞬间降下来了。

下面说一下我对这个问题的解决思想。很简单,遍历两个数列,一个变量sum记录对应位相加结果,一个变量carry记录进位,将sum取个位数作为这两个位相加的结果。遍历数列l1l2最终会得到三个结果:

  1. l1l2长度相同,同时遍历完
  2. l1先遍历完,l2较长
  3. l2先遍历完,l1较长

一开始我思想比较死板,严格将每种情况都用if语句乖乖地写下来了,但是现在我却想说:“管他呢!”。哪个先遍历完,就把它的指针设为NULL,在两个数列都没遍历完(不都是NULL)之前,sum只加上非NULL指针的val值就行了。最后等把两个数列都遍历完后,再看看是否产生了更高的进位即可。

现在思路清楚了,首先做准备工作
这里写图片描述
其实没必要设置ptr1ptr2的,直接对参数取next也是可以的,因为这里是参数的副本,在这里的操作对参数本身是没有任何影响的,但是我觉得还是养成不碰参数的习惯较好。ptrForAnswer是为了扩展answer而准备的,最后函数结束返回answer即可。

接下来可以确定循环了
这里写图片描述

如何规避数列长度不一的情况呢,这是我自认为做得比较好的办法:

int sum = carry;
if (ptr1) {
    sum += ptr1->val;
    ptr1 = ptr1->next;
    }
if (ptr2) {
    sum += ptr2->val;
    ptr2 = ptr2->next;
    }

if语句做了两件事情,一、将数列指针移向下一位(有下一位的前提下);二、把该位的值并入了sum中(该数列存在这一位的前提下)。

将两个数列都遍历完后,该考虑是否最后还产生了一个进位,这很简单
这里写图片描述

如此这一个问题就解决了。速度也还不错。这个算法复杂度主要看参数l1l2的长度,因此是O(n)
这里写图片描述

个人而言,这个问题有两个亮点,第一就是它的反向存储,第二就是我对数列长度不一的问题的优化。详细可以看下面的源代码。

/* 问题描述
    两个链式非负整数,存储是反方向的。
    如4->3->2其实是234,要求计算出两个链式数据的和,
    并按相同存储方式返回
    */
/* From  https://leetcode.com/problems/add-two-numbers/description/ */

/* 2017-09-07 by 王世祺 */

/* build the struct ListNode */
#include<iostream>
using namespace std;

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    int carry = 0;
    ListNode* ptr1 = l1->next;
    ListNode* ptr2 = l2->next;
    ListNode* answer = new ListNode((l1->val + l2->val) % 10);
    ListNode* ptrForAnswer = answer;
    carry = (l1->val + l2->val) >= 10 ? 1 : 0;

    /* 在两个数长度不一样的时候,和计算只在相同长度范围内有效。 */ 
    while (ptr1 || ptr2) {
        int sum = carry;
        if (ptr1) {
            sum += ptr1->val;
            ptr1 = ptr1->next;
        }
        if (ptr2) {
            sum += ptr2->val;
            ptr2 = ptr2->next;
        }
        ptrForAnswer->next = new ListNode(sum % 10);
        carry = (sum) >= 10 ? 1 : 0;
        ptrForAnswer = ptrForAnswer->next;
    }

    /* 结束,看是否有进位 */
    if (carry == 1)
        ptrForAnswer->next = new ListNode(1);

    return answer;
}

int main() {
    ListNode* l1 = new ListNode(3);
    l1->next = new ListNode(7);
    ListNode* l2 = new ListNode(9);
    l2->next = new ListNode(2);
    addTwoNumbers(l1, l2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值