【LeetCode #2 題解】兩數相加

一、題目

給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。

如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。

您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。

示例:

輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
原因:342 + 465 = 807

二、笨方法題解(因爲存在數據大小侷限性,所以提交時報錯誤)

剛拿到這個問題,第一想法就是這個笨方法,實測結果小沒問題,但一旦數據很大時,會出現超出內存,
所以提交 leetcode 時,自然報錯。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int get_list_value(struct ListNode * l)
{
    unsigned long long  sum = 0, temp=1;
    struct ListNode *p = l;

    while(p != NULL){
        sum = sum + p->val * temp;
        p = p->next;

        if(p!=NULL)
            temp *= 10;
    }
    return sum;
}

struct ListNode* int_to_list(unsigned long long value)
{
    struct ListNode *result=NULL, *end_l=NULL, *temp=NULL;
    unsigned long long  value_t = value;

    while(value_t / 10 > 0){ 

        temp = (struct ListNode *)malloc(sizeof(struct ListNode));
        temp->val = value_t % 10; 
        temp->next= NULL;
        value_t = value_t / 10;  

        if(result == NULL){
            result = temp;
        }else{
            end_l->next = temp;
        }
        end_l =temp;
    }

    temp = (struct ListNode *)malloc(sizeof(struct ListNode));
    temp->val = value_t % 10; 
    temp->next= NULL;
  
    if(result == NULL){
        result = temp;
    }else{
        end_l->next = temp;
    }
    end_l = temp;

    return result;
}

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    unsigned long long  sum = 0;
    struct ListNode *result;

    sum = get_list_value(l1) + get_list_value(l2);

    return int_to_list(sum);
}

2.1 測試結果(小數據時計算正常)

在這裏插入圖片描述

2.2 測試結果(大數據時計算錯誤)

繼續增大數據,將前面的數據從1 改 爲 9,
此時,實際結果就出問題了,並不是正確的answer。
在這裏插入圖片描述


三、正解(無侷限性)

詳細的解題思路,我都寫在註釋裏了,可以根據代碼註釋理解下。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode* new_ListNode(int val)
{
    struct ListNode *new_l = (struct ListNode *)malloc(sizeof(struct ListNode));
    new_l->val = val;
    new_l->next = NULL;

    return new_l;
}

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){

    struct ListNode *p_l1=l1, *p_l2=l2;
    struct ListNode *result=NULL, *temp_l=NULL, *end_l=NULL, *temp_p=NULL;
    int val_t=0, advance_flag=0;

    // 開始根據鏈表元素按位相加
    // 此時分兩種情況:
    // 1. L1 和 L2 鏈表當前節點均不爲空,將兩數據相加保存到result鏈表中,同時判斷是否要進位
    while( (NULL != p_l1) && (NULL != p_l2) ){

        // 計算時加上進位標誌位
        val_t = p_l1->val + p_l2->val + advance_flag; 

        // 清除進位標誌位
        advance_flag = 0;

        // 如果計算值大於10 ,說明需要進位,設置進位標誌位
        if(val_t >= 10){
            advance_flag = 1;
        }

        // 將當前值保存到新 list 中
        val_t = val_t % 10;
        temp_l = new_ListNode(val_t);   // 新建一個list 節點
        if(NULL == result){             // 頭結點
            result = temp_l; 
        }else{
            end_l->next = temp_l;
        }
        end_l = temp_l;

        // 開始下一節點判斷
        p_l1 = p_l1->next;
        p_l2 = p_l2->next;
    }

    // 2. L1 和 L2 中有一項爲NULL時,直接保存到result
    if( ( (NULL == p_l1) && (NULL != p_l2) ) || ( (NULL != p_l1) && (NULL == p_l2) )  ){

        // 得到非空的 listnode
        temp_p = (p_l1 != NULL) ? p_l1 : p_l2;

        while(temp_p != NULL){
            
            val_t = temp_p->val + advance_flag;
            // 清除進位標誌位
            advance_flag = 0;

            // 如果計算值大於10 ,說明需要進位,設置進位標誌位
            if(val_t >= 10){
                advance_flag = 1;
            }

            // 將當前值保存到新 list 中
            val_t = val_t % 10;
            temp_l = new_ListNode(val_t);   // 新建一個list 節點
            if(NULL == result){             // 頭結點
                result = temp_l; 
            }else{
                end_l->next = temp_l;
            }
            end_l = temp_l;

            // 開始下一節點判斷
            temp_p = temp_p->next;
        }   
    }

    // 3. 跑到這裏說明是已經判斷到最後一個節點了,此時只需要考慮進位的問題了
    if(advance_flag == 1){
        temp_l = new_ListNode(advance_flag);   // 新建一個list 節點

        if(NULL == result){             // 頭結點
            result = temp_l; 
        }else{
            end_l->next = temp_l;
        }
        end_l = temp_l;
    }

    return result;
}


四、提交結果

在這裏插入圖片描述
通過結果看還不錯,內存最優,但運行時間並非最優,


五、別人的更優答案

從 leetcode 上看到了有更快的 8 ms,思路一樣,但他的代碼寫的更加簡潔
地址:《faster than 98.46% of C

此處附上,可以學習下

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
    struct ListNode *head = NULL;
    struct ListNode *curNode = NULL;
    
    //記錄上一個節點的和是否有進位
    int addNum = 0;
    //每個節點當前位的和
    int sum = 0;
    
    while (l1!=NULL || l2!=NULL || addNum==1) {
        
        struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
        node->val = 0;
        node->next = NULL;
        
        if (l1!=NULL&&l2!=NULL) {
            //兩個鏈表長度都覆蓋到的節點
            sum = l1->val + l2->val;
            sum += addNum;
            int val = sum%10;
            node->val = val;
            
            l1 = l1->next;
            l2 = l2->next;
            
        }else if (l1!=NULL) {
            
            sum = l1->val;
            sum += addNum;
            int val = sum%10;
            node->val = val;
            
            l1 = l1->next;
            
        }else if (l2!=NULL) {
            
            sum = l2->val;
            sum += addNum;
            int val = sum%10;
            node->val = val;
            
            l2 = l2->next;
            
        }else {//兩個鏈表都遍歷結束但尾節點相加還有進位
            node->val = 1;
            addNum = 0;
            sum = 0;
        }
        
        addNum = sum>=10 ? 1 : 0;
        
        if(head==NULL){
            head = node;
        }else{
            curNode->next = node;
        }
        curNode = node;
    }
    
    return head;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章