LeetCode算法问题1 —— Add Two Numbers

来源:互联网 发布:淘宝网站策划 编辑:程序博客网 时间:2024/05/29 15:01

最近在学校提供网站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);}