002. 兩數相加 | Leetcode題解

2021-03-02 前端布道師
題目描述

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

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

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

示例:

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

難度:支持語言:JavaScriptJavaPythonC++相關標籤相關企業思路與算法設立一個表示進位的變量 carried,建立一個新鍊表,把輸入的兩個鍊表從頭往後同時處理,每兩個相加,將結果加上 carried 後的值作為一個新節點到新鍊表後面,並更新 carried 值即可。2.addTwoNumbers

(圖片來自:https://github.com/MisterBooo/LeetCodeAnimation)

實際上兩個大數相加也是一樣的思路, 只不過具體的操作 API 不一樣而已。這種題目思維難度不大,難點在於心細,因此大家一定要自己寫一遍,確保 bug free。

由於輸入的兩個鍊表都是逆序存儲數字的位數的,因此兩個鍊表中同一位置的數字可以直接相加。

我們同時遍歷兩個鍊表,逐位計算它們的和,並與當前位置的進位值相加。具體而言,如果當前兩個鍊表處相應位置的數字為 n1,n2n1,n2,進位值為 \textit{carry}carry,則它們的和為 n1+n2+\textit{carry}n1+n2+carry;其中,答案鍊表處相應位置的數字為 (n1+n2+\textit{carry}) % 10(n1+n2+carry)%10,而新的進位值為 \lfloor\frac{n1+n2+\textit{carry}}{10}\rfloor⌊10n1+n2+carry⌋。

如果兩個鍊表的長度不同,則可以認為長度短的鍊表的後面有若干個 00 。

此外,如果鍊表遍歷結束後,有 \textit{carry} > 0carry>0,還需要在答案鍊表的後面附加一個節點,節點的值為 \textit{carry}carry。

關鍵點解析

用一個 carried 變量來實現進位的功能,每次相加之後計算 carried,並用於下一位的計算

複雜度分析空間複雜度:O(\max(m,n))O(max(m,n))。答案鍊表的長度最多為較長鍊表的長度 +1+1。代碼JavaScript 實現
/**
 * @來源:Javascript中文網 - 前端進階資源教程 https://www.javascriptc.com/
 * @介紹:前端中文網是以前端進階資源教程分享為主的專業網站,包括:前端、大廠面試題、typescript教程、程序人生、React.js
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function (l1, l2) {
  if (l1 === null || l2 === null) return null;

  // 使用dummyHead可以簡化對鍊表的處理,dummyHead.next指向新鍊表
  let dummyHead = new ListNode(0);
  let cur1 = l1;
  let cur2 = l2;
  let cur = dummyHead; // cur用於計算新鍊表
  let carry = 0; // 進位標誌

  while (cur1 !== null || cur2 !== null) {
    let val1 = cur1 !== null ? cur1.val : 0;
    let val2 = cur2 !== null ? cur2.val : 0;
    let sum = val1 + val2 + carry;
    let newNode = new ListNode(sum % 10); // sum%10取模結果範圍為0~9,即為當前節點的值
    carry = sum >= 10 ? 1 : 0; // sum>=10,carry=1,表示有進位
    cur.next = newNode;
    cur = cur.next;

    if (cur1 !== null) {
      cur1 = cur1.next;
    }

    if (cur2 !== null) {
      cur2 = cur2.next;
    }
  }

  if (carry > 0) {
    // 如果最後還有進位,新加一個節點
    cur.next = new ListNode(carry);
  }

  return dummyHead.next;
};

/**
*  @作者:LeetCode-Solution
*  @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-leetcode-solution/
*/
var addTwoNumbers = function(l1, l2) {
    let head = null, tail = null;
    let carry = 0;
    while (l1 || l2) {
        const n1 = l1 ? l1.val : 0;
        const n2 = l2 ? l2.val : 0;
        const sum = n1 + n2 + carry;
        if (!head) {
            head = tail = new ListNode(sum % 10);
        } else {
            tail.next = new ListNode(sum % 10);
            tail = tail.next;
        }
        carry = Math.floor(sum / 10);
        if (l1) {
            l1 = l1.next;
        }
        if (l2) {
            l2 = l2.next;
        }
    }
    if (carry > 0) {
        tail.next = new ListNode(carry);
    }
    return head;
};

/**
*  @作者:afeng-xiu - 力扣(LeetCode)
*  @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-ge-shu-xiang-jia-zui-rong-yi-li-jie-de-jie-f/
*/
var addTwoNumbers = function(l1, l2) {
    let addOne = 0
    let sum = new ListNode('0')
    let head = sum
    while (addOne || l1 || l2) {
        let val1 = l1 !== null ? l1.val : 0 // 優化點
        let val2 = l2 !== null ? l2.val : 0 //優化點
        let r1 = val1 + val2 + addOne
        addOne = r1 >= 10 ? 1 : 0
        sum.next = new ListNode(r1 % 10)
        sum = sum.next
        if (l1) l1 = l1.next
        if (l2) l2 = l2.next
    }
    return head.next
};

C++ 實現:

C++代碼與上面的 JavaScript 代碼略有不同:將 carry 是否為 0 的判斷放到了 while 循環中

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* ret = nullptr;
        ListNode* cur = nullptr;
        int carry = 0;
        while (l1 != nullptr || l2 != nullptr || carry != 0) {
            carry += (l1 == nullptr ? 0 : l1->val) + (l2 == nullptr ? 0 : l2->val);
            auto temp = new ListNode(carry % 10);
            carry /= 10;
            if (ret == nullptr) {
                ret = temp;
                cur = ret;
            }
            else {
                cur->next = temp;
                cur = cur->next;
            }
            l1 = l1 == nullptr ? nullptr : l1->next;
            l2 = l2 == nullptr ? nullptr : l2->next;
        }
        return ret;
    }
};

/**
*  @作者:chenlele
*  @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-gpe3dbjds1/
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int len1=1;//記錄l1的長度
        int len2=1;//記錄l2的長度
        ListNode* p=l1;
        ListNode* q=l2;
        while(p->next!=NULL)//獲取l1的長度
        {
            len1++;
            p=p->next;
        }
        while(q->next!=NULL)//獲取l2的長度
        {
            len2++;
            q=q->next;
        }
        if(len1>len2)//l1較長,在l2末尾補零
        {
            for(int i=1;i<=len1-len2;i++)
            {
                q->next=new ListNode(0);
                q=q->next;
            }
        }
        else//l2較長,在l1末尾補零
        {
            for(int i=1;i<=len2-len1;i++)
            {
                p->next=new ListNode(0);
                p=p->next;
            }
        }
        p=l1;
        q=l2;
        bool count=false;//記錄進位
        ListNode* l3=new ListNode(-1);//存放結果的鍊表
        ListNode* w=l3;//l3的移動指針
        int i=0;//記錄相加結果
        while(p!=NULL&&q!=NULL)
        {
            i=count+p->val+q->val;
            w->next=new ListNode(i%10);
            count=i>=10?true:false;
            w=w->next;
            p=p->next;
            q=q->next;
        }
        if(count)//若最後還有進位
        {
            w->next=new ListNode(1);
            w=w->next;
        }
        return l3->next;
    }
};

/**
*  @作者:dnanki
*  @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/di-gui-si-lu-jian-dan-dai-ma-duan-by-dnanki/
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return dfs(l1, l2, 0);
    }

    ListNode dfs(ListNode l, ListNode r, int i) {
        if (l == null && r == null && i == 0) return null;
        int sum = (l != null ? l.val : 0) + (r != null ? r.val : 0) + i;
        var node = new ListNode(sum % 10);
        node.next = dfs(l != null ? l.next : null, r != null ? r.next : null, sum / 10);
        return node;
    }
}


Java 實現:
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        int carry = 0;

        while(l1 != null || l2 != null)
        {
            int sum = carry;
            if(l1 != null)
            {
                sum += l1.val;
                l1 = l1.next;
            }
            if(l2 != null)
            {
                sum += l2.val;
                l2 = l2.next;
            }
            // 創建新節點
            carry = sum / 10;
            cur.next = new ListNode(sum % 10);
            cur = cur.next;

        }
        if (carry > 0) {
            cur.next = new ListNode(carry);
        }
        return dummyHead.next;
    }
}

/**
*  @作者:Sophia_fez
*  @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/mo-ni-jia-fa-tong-yong-mo-ban-by-sophia_fez/
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = new ListNode(0), cur = head;
        int carry = 0;

        while (l1 != null || l2 != null) {
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + carry;
            carry = sum / 10;
            cur.next = new ListNode(sum % 10);
            cur = cur.next;
            if (l1 != null) l1 = l1.next;
            if (l2 != null) l2 = l2.next;
        }
        if (carry > 0)  cur.next = new ListNode(carry);

        return head.next;
    }
}

Python 實現:
class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        res=ListNode(0)
        head=res
        carry=0
        while l1 or l2 or carry!=0:
            sum=carry
            if l1:
                sum+=l1.val
                l1=l1.next
            if l2:
                sum+=l2.val
                l2=l2.next
            # set value
            if sum<=9:
                res.val=sum
                carry=0
            else:
                res.val=sum%10
                carry=sum//10
            # creat new node
            if l1 or l2 or carry!=0:
                res.next=ListNode(0)
                res=res.next
        return head

# @作者:meng-zhi-hen-n
# @連結:https://leetcode-cn.com/problems/add-two-numbers/solution/zui-zhi-bai-de-xie-fa-by-meng-zhi-hen-n-2/
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        # 創建一個結點值為 None 的頭結點, dummy 和 p 指向頭結點, dummy 用來最後返回, p 用來遍歷
        dummy = p = ListNode(None)
        s = 0               # 初始化進位 s 為 0
        while l1 or l2 or s:
            # 如果 l1 或 l2 存在, 則取l1的值 + l2的值 + s(s初始為0, 如果下面有進位1, 下次加上)
            s += (l1.val if l1 else 0) + (l2.val if l2 else 0)
            p.next = ListNode(s % 10)       # p.next 指向新鍊表, 用來創建一個新的鍊表
            p = p.next                      # p 向後遍歷
            s //= 10                        # 有進位情況則取模, eg. s = 18, 18 // 10 = 1
            l1 = l1.next if l1 else None    # 如果l1存在, 則向後遍歷, 否則為 None
            l2 = l2.next if l2 else None    # 如果l2存在, 則向後遍歷, 否則為 None
        return dummy.next   # 返回 dummy 的下一個節點, 因為 dummy 指向的是空的頭結點, 下一個節點才是新建鍊表的後序節點

拓展

通過單鍊表的定義可以得知,單鍊表也是遞歸結構,因此,也可以使用遞歸的方式來進行 reverse 操作。

由於單鍊表是線性的,使用遞歸方式將導致棧的使用也是線性的,當鍊表長度達到一定程度時,遞歸可能會導致爆棧,

算法將兩個鍊表的第一個節點值相加,結果轉為 0-10 之間的個位數,並設置進位信息將第一步得到的頭節點的 next 指向第二步返回的鍊表C++實現
// 普通遞歸
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        return addTwoNumbers(l1, l2, 0);
    }

private:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2, int carry) {
        if (l1 == nullptr && l2 == nullptr && carry == 0) return nullptr;
        carry += (l1 == nullptr ? 0 : l1->val) + (l2 == nullptr ? 0 : l2->val);
        auto ret = new ListNode(carry % 10);
        ret->next = addTwoNumbers(l1 == nullptr ? l1 : l1->next,
                                 l2 == nullptr ? l2 : l2->next,
                                 carry / 10);
        return ret;
    }
};
// 類似尾遞歸
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = nullptr;
        addTwoNumbers(head, nullptr, l1, l2, 0);
        return head;
    }

private:
    void addTwoNumbers(ListNode*& head, ListNode* cur, ListNode* l1, ListNode* l2, int carry) {
        if (l1 == nullptr && l2 == nullptr && carry == 0) return;
        carry += (l1 == nullptr ? 0 : l1->val) + (l2 == nullptr ? 0 : l2->val);
        auto temp = new ListNode(carry % 10);
        if (cur == nullptr) {
            head = temp;
            cur = head;
        } else {
            cur->next = temp;
            cur = cur->next;
        }
        addTwoNumbers(head, cur, l1 == nullptr ? l1 : l1->next, l2 == nullptr ? l2 : l2->next, carry / 10);
    }
};

其他說明:[leetcode 題解 | 每日一題🔥] 所有題目並非全部為本人解答,部分為在複習學習中整理提取其他解題作者的優秀筆記,便於大家學習共同進步,如有侵權,請聯繫刪除。合作方:JavaScript中文網 – 全球極客摯愛的技術成長平臺原題leetcode連結:2.add-two-numbers](https://leetcode-cn.com/problems/add-two-numbers/)

相關焦點

  • 整數反轉 | Leetcode題解
    每次找到當前數的最後一位,然後作為反轉數字的第一位,例如123:123 --> 0*10  + 312  --> 3*10  + 21   --> 32*10 + 1再注意保存開始的正負狀態和結果的限制[−2^31, 2^31 − 1]。思路 2:本題如果不考慮溢出問題,是非常簡單的。
  • LeetCode Weekly Contest 224 題解
    --InterviewProblem/blob/master/LeetCode/leetcode_number-of-rectangles-that-can-form-the-largest-square.cpp題解:模擬題。
  • Leetcode刷題練Python - 2. 兩數相加
    題目:兩數相加(中等難度)給出兩個 非空 的鍊表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。如果,我們將這兩個數相加起來,則會返回一個新的鍊表來表示它們的和。您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
  • LeetCode Weekly Contest 204 題解
    /blob/master/LeetCode/leetcode_detect-pattern-of-length-m-repeated-k-or-more-times.cpp題解:模擬或者是單純的數組題。
  • 三數之和 | Leetcode題解
    1, 2, -1, -4\],滿足要求的三元組集合為:\[  \[-1, 0, 1\],  \[-1, -1, 2\]\]難度:支持語言:JavaScript、Python、C++相關標籤相關企業思路採用分治的思想找出三個數相加等於
  • 兩個二進位數相加
    陸小鳳原創本文講解「兩個二進位數相加」的算法題。
  • 笨方法刷 leetcode(一)
    但是,數組中同一個元素不能使用兩遍。     示例:  給定 nums = [2, 7, 11, 15], target = 9  因為 nums[0] + nums[1] = 2 + 7 = 9 原題連結:https://leetcode-cn.com/problems/two-sum/解決思路:用第1個數字依次與其後面的數字相加
  • 推薦4個基於 Java語言的開源 Leetcode 題解!算法面試不愁了!
    為了能夠幫助我們更好的刷 Leetcode,Guide 精選了一些不錯的基於 Java 題解的開源項目,文末有項目連結。下面的項目是根據下面三個標準選出:項目的質量如何,這一點可以從 star、issue 以及 pr 的數量側面反映出來。
  • 盛最多水的容器 | Leetcode題解
    ,a``n,每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) 。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。**說明:**你不能傾斜容器。
  • LeetCode題解—斐波那契數列
    斐波那契數列的定義如下:F(0) = 0F(1) = 1 F(N) = F(N - 1) + F(N - 2)其中 N > 1.斐波那契數列由 0 和 1 開始,之後的斐波那契數就是由之前的兩數相加而得出
  • leetcode之字符串相加
    序本文主要記錄一下leetcode之字符串相加題目給定兩個字符串形式的非負整數 num1 和num2 ,計算它們的和。
  • 【Leetcode刷題練Python】1. 兩數之和
    官方網址是:http://leetcode.com/,打開瀏覽器輸入網址,就可以在線寫代碼、提交、查看算法的優略以及個人別實現算法的對比等等。開箱即用,非常方便。上面收錄的算法題目,大多來自大公司的筆試面試題,分為簡單、中等、困難三個等級,從基礎到高級都有。
  • 每天一道leetcode18-四數之和
    正當班主任要繼續發話,只聽到角落默默想起來一個聲音:」K2」前言 2018.11.20號打卡今天的題目leetcode61:https://leetcode-cn.com/problems/rotate-list/description/昨天的題解題目 每天一道leetcode18- 四數之和分類:雙指針
  • leetcode-cheatsheet 史詩級更新,來看看我是如何寫題解的
    ,插件現在內置題解模板功能,目前模板只有一套,這套模板是「我經常使用的題解模板」。最後祝大家寫出漂亮的題解!體驗優化「路由」記憶現在增加「路由」記憶功能。比如上面提到的「寫題解」功能,當你點擊寫題解按鈕後,會「自動定位到題解模板標籤頁」網站寬度調整之前考慮的僅僅是插件中使用,而現在除了在插件中運行,還可以在 PC 端運行。因此我增加了一個打包命令專門用來打包到 「PC 端」。
  • LeetCode-1.兩數之和(Two Sum)
    相信每個想刷leetcode的人,第一次打開網站,就從這道題開始的。一起起航吧,Great Voyage.
  • python 算法(8) 兩數和問題
    python 算法(8) 兩數和問題問題問題來自leecode:問題描述:給定一個整數數組 nums
  • 四數之和(18)-四數之和(454)-LeetCode
    滿足要求的四元組集合為:[[-1,  0, 0, 1], [-2, -1, 1, 2], [-2,  0, 0, 2]]來源:LeetCode連結:https://leetcode-cn.com/problems/4sum(1)排序+雙指針    延續兩數之和、三數之和的雙指針+排序解題思路
  • LeetCode 熱題 HOT 100(01,兩數相加)
    LeetCode 熱題 HOT 100(01,兩數相加)不夠優秀,發量尚多,千錘百鍊,方可成佛。算法的重要性不言而喻,無論你是研究者,還是最近比較火熱的IT 打工人,都理應需要一定的算法能力,這也是面試的必備環節,算法功底的展示往往能讓面試官眼前一亮,這也是在大多數競爭者中脫穎而出的重要影響因素。
  • leetcode-15三數之和
    三數之和:https://leetcode-cn.com/problems/3sum/1、題目描述
  • LeetCode-14. Longest Common Prefix | 最長公共前綴
    return strs[0] } res := strs[0] //獲取字符串數組裡的第一個元素 for _, v := range strs[1:] { //從字符串數組第二個元素開始遍歷 var i int for ; i < len(v) && i < len(res); i++ { //遍歷兩數組裡的元素