题目大意:给定两个非空链表,分别代表两个非负整数。注意是以倒序的方式存储,并且一个节点存储一位数字。要求将这两个数相加,并将结果以链表的形式返回。
说明:这两个数均不包含前导的0,除非这个数就是0。
示例:输入链表:(2 -> 4 -> 3) 和链表(5 -> 6 -> 4),代表数字342与465求和,结果为807,输出形式:7 -> 0 -> 8,即倒序输出。
解题思路:刚读完题的第一想法很简单:既然是数相加,就直接读取链表,将对应的数赋给两个变量,再将其相加,最后将结果以倒序的方式输出就行了。但很快就意识到这样做并不好。一是题目没有交代数的大小,即使采用int64存储,仍然可能超范围,其次这是一道链表的题,应当用链表相关的知识去解决。
随后我思考解决的方案。既然是相加,那就将对应位上的数相加,求模10解,有进位的话用标志位记录,加到下一位上。具体操作起来还有一些细节:一是两个数可能位数不一致,有的位上不能直接相加,需要先对缺失的位补0;其次是最后一位相加之后可能会向前再进一位,需要做特别处理。经过编写与本地调试之后,获得了AC。如下所示。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int get_length(ListNode* l){
int i;
for(i=1;;i++)
if(l->next!=NULL)
l=l->next;
else
return i;
}
ListNode* enhance(ListNode* l, int len){
int t=len-get_length(l);
if(t==0)
return l; //若本身足够长,则无需修改
else
{
ListNode *p=l;
while(p->next!=NULL)
{
p=p->next;
}
for(int i=0;i<t;i++)
{
p->next=new ListNode(0);
p=p->next;
}
return l;
}
}
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int leng2,adder,flag;
ListNode *res, head(0);
leng2=max(get_length(l1),get_length(l2));
enhance(l1,leng2);
enhance(l2,leng2);
flag=0;
res=&head; //用res记录头部节点
for(int i=0;i<leng2;i++) //对应位相加,赋给新建立的节点
{
adder=l1->val+l2->val+flag;
res->next=new ListNode(adder%10);
res=res->next;
if(i!=leng2-1)
{
l1=l1->next;
l2=l2->next;
}
flag=adder/10;
}
if(flag) res->next=new ListNode(1); //表明要多一位
else
res->next=NULL;
return head.next;
}
};
由于缺少经验,因此在书写代码时费了一番功夫。因为这是一道链表的小应用题,因此我并没有去专门的建立一个链表类,而是利用它给出的结构体定义进行了相关的操作。get_length()函数用于返回链表长度,enhance()函数用于将两链表扩充成一样的长度。最后在addTwoNumbers中将它们相加。这里,推荐创建一个头结点,然后通过一个指向其的指针对链表进行遍历等操作。
不难发现 , 最后AC的代码比较冗长,而且在几处遍历了整个链表,比较耗时。在查看了discuss中的高票答案(如下)后,我发现了存在的几处提升空间:
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode preHead(0), *p = &preHead;
int extra = 0;
while (l1 || l2 || extra) {
int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + extra;
extra = sum / 10;
p->next = new ListNode(sum % 10);
p = p->next;
l1 = l1 ? l1->next : l1;
l2 = l2 ? l2->next : l2;
}
return preHead.next;
}
一是无需做复杂的填0操作,而是通过活用?:运算符,在两位相加时判断加数是否为空,如果是NULL,则认为其值是0即可 ;二是无需另外做高位进一处理,而是将循环结束条件修改为(l1 || l2 || extra)。这样一来,就大大减少了代码量,并且轻松获得AC。
事实证明 ,一些小的处理能够大大简化代码的书写。在做题时,不妨多思考,再落笔。