今天考完了计组,闲来无事突然想刷一下LeetCode,毕竟自己实在太菜了,需要多加练习才行啊……
题目描述:
You are given two non-empty linked lists representing two non-negative integers. 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.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8 Explanation: 342 + 465 = 807.
一道简单的LeetCode第二题,然而其实在我一开始学数据结构的时候困扰了很久,因为我以为要把这个数据结构倒置……(不得不佩服我的一根筋)这学期学完数据结构和计组再来做,居然一下就想到了。完全应该直接就像加法一样的步骤。所以我以线性思维写了如下的愚蠢的代码,十分冗长:
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { int temp, overflow, flag = 0; struct ListNode* l3, *head, *q, *p; head = (struct ListNode*)malloc(sizeof(struct ListNode)); head->next = NULL; p = head; while(l1 != NULL && l2 != NULL){ if(flag == 0) temp = l1->val + l2->val; else temp = l1->val + l2->val + overflow; if(temp < 10){ flag = 0; q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp; q->next = NULL; p->next = q; p = p->next; l1 = l1->next; l2 = l2->next; } else{ overflow = 1; flag = 1; q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp - 10; q->next = NULL; p->next = q; p = p->next; l1 = l1->next; l2 = l2->next; } } if(l1 == NULL && l2 ==NULL){ if(flag == 1){ q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = 1; q->next = NULL; p->next = q; p = p->next; } return head->next; } else if(l1 != NULL && l2 ==NULL){ if(flag == 0){ p->next = l1; return head->next; } else { while(l1 != NULL){ temp = overflow + l1->val; if(temp < 10){ q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp; q->next = l1->next; p->next = q; p = p->next; return head->next; } else{ overflow = 1; q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp -10; q->next = NULL; p->next = q; p = p->next; l1 = l1->next; } } q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = 1; q->next = NULL; p->next = q; p = p->next; return head->next; } } else{ if(flag == 0){ p->next = l2; return head->next; } else { while(l2 != NULL){ temp = overflow + l2->val; if(temp < 10){ q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp; q->next = l2->next; p->next = q; p = p->next; return head->next; } else{ overflow = 1; q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = temp - 10; q->next = NULL; p->next = q; p = p->next; l2 = l2->next; } } q = (struct ListNode*)malloc(sizeof(struct ListNode)); q->val = 1; q->next = NULL; p->next = q; p = p->next; return head->next; } } return head->next; }
其原理就是if-else分类,分类里再加循环。但是仔细观察可以发现,这个代码重复的部分太多,不符合“高内聚,低耦合”的设计思想。比如我居然对temp大于10还是小于10分类,这个完全可以用mod取余来解决,白白增加了代码量。而overflow和flag的设计更是多余,完全可以直接用结果temp/10来解决。更可悲的是,我一层层分类,分类到了l1不空l2空这种情况,而这种情况的区别只是不用加l1的值了,那岂不是直接在大while里加if就行了……
再来看LeetCode discuss里vote数第一的答案,就比我简洁很多:
public class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode c1 = l1; ListNode c2 = l2; ListNode sentinel = new ListNode(0); ListNode d = sentinel; int sum = 0; while (c1 != null || c2 != null) { sum /= 10; if (c1 != null) { sum += c1.val; c1 = c1.next; } if (c2 != null) { sum += c2.val; c2 = c2.next; } d.next = new ListNode(sum % 10); d = d.next; } if (sum / 10 == 1) d.next = new ListNode(1); return sentinel.next; } }
其主要思想在于,只用了一个while循环,终止条件便是两链表都为空的时候。而在这个循环里面,通过对c1 c2是否为空的if语句来执行不同的操作。他的简洁之处在于利用sum/10便可以把当前位运算后给下一位的进位数很好的表示出来,只用了一个变量sum。唉,说白了这就是一个sum/10和%10的操作,我非得整那么复杂做什么呢
优化后的C代码:
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { int sum = 0; struct ListNode *head, *p; head = (struct ListNode*)malloc(sizeof(struct ListNode)); head->next = NULL; p = head; while(l1 != NULL || l2 != NULL){ sum /= 10; if(l1 != NULL){ sum += l1->val; l1 = l1->next; } if(l2 != NULL){ sum += l2->val; l2 = l2->next; }
//这里其实就不需要再用个指针q了,直接通过p->next操作即可 p->next = (struct ListNode*)malloc(sizeof(struct ListNode)); p->next->val = sum % 10; p->next->next = NULL; p = p->next; } if(sum/10 == 1){ p->next = (struct ListNode*)malloc(sizeof(struct ListNode)); p->next->val = 1; p->next->next = NULL; } return head->next; }
通过对比,我可以发现,C语言更零碎,需要管理的东西更多,比如申请空间,赋值,令指针为空;而java只需要一个new即可。
该算法与我一开始的垃圾算法比,精髓在于做了更好的分类。在这个while条件里用if决定加不加某个链表中的结点值。同时最后需要考虑溢出。总结起来就是大类里分小类,执行操作最大程度化简。