2. Add Two Numbers
You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
题目的意思是将两个非负数放到两个链表list1和list2,链表的存储方式是高位在右低位在左(类似小端模式存储,这样的效果是往右边进位),而且每个节点都只能饿保存一位数。然后需要设计一个算法将两个非负数相加,将结果以之前规则的链表形式返回。题目很直接,没有啥让人疑惑的地方,下面开始第一次尝试:
为了减少内存的分配次数,最开始的想法是先求出两个链表的长度,使用长的来保存计算出来的结果,如果最后有进位的话只需要重新分配一个节点的内存。代码如下:
1 struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { 2 struct ListNode *head, *p, *res = l1; 3 struct ListNode *q, *tmp = l2; 4 5 int l1Length = 0; 6 int l2Length = 0; 7 p = l1; 8 q = l2; 9 while(1) 10 { 11 if(l1) 12 { 13 l1 = l1->next; 14 l1Length++; 15 } 16 if(l2) 17 { 18 l2 = l2->next; 19 l2Length++; 20 } 21 if(!(l1 || l2)) 22 break; 23 if( !(l1Length == l2Length)) 24 break; 25 } 26 if(l2Length > l1Length) 27 { 28 res = q; 29 tmp = p; 30 } 31 32 head = res; 33 34 int carry = 0; 35 p = res; 36 while(1) 37 { 38 if(tmp) 39 { 40 res->val += tmp->val + carry; 41 carry = res->val / 10; 42 res->val = res->val % 10; 43 44 p = res; 45 res = res->next; 46 tmp = tmp->next; 47 } 48 else 49 { 50 if(res) 51 { 52 res->val += carry; 53 carry = res->val / 10; 54 res->val = res->val % 10; 55 p = res; 56 res = res->next; 57 } 58 else if(carry) 59 { 60 struct ListNode *cur = (struct ListNode*)malloc(sizeof(struct ListNode)); 61 cur->val = carry; 62 cur->next = NULL; 63 p->next = cur; 64 return head; 65 } 66 else 67 return head; 68 } 69 } 70 return NULL; 71 }
代码中用res指针来指向长的那个链表,作为保存结果的链表,用carry来记录是否进位,运行的结果是:
Runtime: 20 ms 这个与最快的16ms还有差距的,于是开始了第二次尝试
第二次尝试的方向删除比较两个链表长度的部分,因为两个链表节点都是相同类型,所以我们可以任意拼接。 代码如下:
1 struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { 2 struct ListNode head, *p; 3 p = &head; 4 5 int carry = 0; 6 7 while(1) 8 { 9 if(l1 && l2) 10 { 11 l1->val += (l2->val + carry); 12 carry = l1->val / 10; 13 l1->val = l1->val % 10; 14 15 p->next = l1; 16 p = l1; 17 18 l1 = l1->next; 19 l2 = l2->next; 20 } 21 else if(l1) 22 { 23 l1->val += carry; 24 carry = l1->val / 10; 25 l1->val = l1->val % 10; 26 27 p->next = l1; 28 p = l1; 29 30 l1 = l1->next; 31 } 32 else if(l2) 33 { 34 l2->val += carry; 35 carry = l2->val / 10; 36 l2->val = l2->val % 10; 37 38 p->next = l2; 39 p = l2; 40 41 l2 = l2->next; 42 } 43 else if(carry) 44 { 45 struct ListNode *cur = (struct ListNode*)malloc(sizeof(struct ListNode)); 46 cur->val = 1; 47 cur->next = NULL; 48 p->next = cur; 49 return head.next; 50 } 51 else 52 return head.next; 53 } 54 return NULL; 55 }
需要注意的是因为我们并不知道哪一个该作为保存结果的list,为了保证wihle循环里的一致性,这个地方的我们先定义一个ListNode的实例head,然后定义一个指向head的指针p,这样while循环里每次得到一个节点都放到p节点后面,然后将该节点的地址赋值给p就行了,使用p来记录result链表的走向,最后返回的head.next就能返回正确结果,运行的结果是:
Runtime: 20 ms 速度竟然没有任何改变!!! 看来比较两个链表长度的操作并没有怎麽耗时,好吧,只能再从别的角度突破了
第三次的尝试方向是再次是否还能继续较少内存,因为list2链表里的内存是等着释放的,所以完全可以使用来存储进位,也就是最后将list1和list2合并成了一个链表,用list2的头结点来保存进位,这样完全可以不用分配内存。代码如下
1 struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { 2 struct ListNode head, *p, *end; 3 p = &head; 4 end = NULL; 5 6 int carry = 0; 7 8 while(1) 9 { 10 if(l1 && l2) 11 { 12 l1->val += (l2->val + carry); 13 carry = l1->val / 10; 14 l1->val = l1->val % 10; 15 16 p->next = l1; 17 p = p->next; 18 l1 = l1->next; 19 !end && (end = l2); 20 l2 = l2->next; 21 } 22 else if(l1) 23 { 24 l1->val += carry; 25 carry = l1->val / 10; 26 l1->val = l1->val % 10; 27 28 p->next = l1; 29 p = p->next; 30 l1 = l1->next; 31 } 32 else if(l2) 33 { 34 l2->val += carry; 35 carry = l2->val / 10; 36 l2->val = l2->val % 10; 37 38 p->next = l2; 39 p = l2; 40 41 l2 = l2->next; 42 } 43 else if(carry) 44 { 45 if( !end ) 46 { 47 end = (struct ListNode*)malloc(sizeof(struct ListNode)); 48 } 49 end->val = 1; 50 end->next = NULL; 51 p->next = end; 52 return head.next; 53 } 54 else 55 return head.next; 56 } 57 return NULL; 58 }
代码中定义了一个指向end的指针,在代码19行将链表2的头结点地址赋值给了end,48~51行表示如果有进位,那麽使用end所指向的list2头结点来保存进位,并加在result结点后面,这样不用分配内存了,运行结果是:
Runtime: 16 ms 终于达到了最快的速度 !!!有图有真相,You are here !