今天考完了计组,闲来无事突然想刷一下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决定加不加某个链表中的结点值。同时最后需要考虑溢出。总结起来就是大类里分小类,执行操作最大程度化简。