2.add-two-numbers.md 7.7 KB
Newer Older
Y
yulecc 已提交
1
## 题目地址(2. 两数相加)
L
lucifer 已提交
2

Y
yulecc 已提交
3
https://leetcode-cn.com/problems/add-two-numbers/
A
arida 已提交
4

A
arida 已提交
5
## 题目描述
L
lucifer 已提交
6

L
luzhipeng 已提交
7
```
Y
yulecc 已提交
8
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
A
arida 已提交
9

Y
yulecc 已提交
10
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
A
arida 已提交
11

Y
yulecc 已提交
12
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
L
luzhipeng 已提交
13

Y
yulecc 已提交
14 15 16 17 18
示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
A
arida 已提交
19 20

```
L
lucifer 已提交
21 22 23 24 25

## 前置知识

- 链表

26 27
## 公司

28 29 30
- 阿里
- 百度
- 腾讯
31

A
arida 已提交
32 33
## 思路

L
lucifer 已提交
34
设立一个表示进位的变量 carried,建立一个新链表,把输入的两个链表从头往后同时处理,每两个相加,将结果加上 carried 后的值作为一个新节点到新链表后面,并更新 carried 值即可。
L
luzhipeng 已提交
35

L
lucifer 已提交
36
![2.addTwoNumbers](https://tva1.sinaimg.cn/large/007S8ZIlly1ghlu6u8jwyg30qh0eon5c.gif)
L
luzhipeng 已提交
37 38 39

(图片来自: https://github.com/MisterBooo/LeetCodeAnimation)

L
lucifer 已提交
40 41
实际上两个大数相加也是一样的思路, 只不过具体的操作 API 不一样而已。这种题目思维难度不大,难点在于心细,因此大家一定要自己写一遍,确保 bug free。

L
luzhipeng 已提交
42 43 44 45
## 关键点解析

1. 链表这种数据结构的特点和使用

L
lucifer 已提交
46
2. 用一个 carried 变量来实现进位的功能,每次相加之后计算 carried,并用于下一位的计算
L
luzhipeng 已提交
47

A
arida 已提交
48
## 代码
L
lucifer 已提交
49

z2014z's avatar
z2014z 已提交
50
- 语言支持:JS,C++,Java,Python
51

z2014z's avatar
z2014z 已提交
52
JavaScript Code:
L
lucifer 已提交
53

L
lucifer 已提交
54
```js
A
arida 已提交
55 56 57 58 59 60 61 62 63 64 65 66
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
L
lucifer 已提交
67 68
var addTwoNumbers = function (l1, l2) {
  if (l1 === null || l2 === null) return null;
69 70

  // 使用dummyHead可以简化对链表的处理,dummyHead.next指向新链表
L
lucifer 已提交
71 72 73 74 75
  let dummyHead = new ListNode(0);
  let cur1 = l1;
  let cur2 = l2;
  let cur = dummyHead; // cur用于计算新链表
  let carry = 0; // 进位标志
76 77

  while (cur1 !== null || cur2 !== null) {
L
lucifer 已提交
78 79 80 81 82 83 84
    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;
85 86

    if (cur1 !== null) {
L
lucifer 已提交
87
      cur1 = cur1.next;
L
lucifer 已提交
88 89
    }

90
    if (cur2 !== null) {
L
lucifer 已提交
91
      cur2 = cur2.next;
92
    }
L
lucifer 已提交
93 94
  }

95 96
  if (carry > 0) {
    // 如果最后还有进位,新加一个节点
L
lucifer 已提交
97
    cur.next = new ListNode(carry);
L
lucifer 已提交
98 99
  }

L
lucifer 已提交
100
  return dummyHead.next;
A
arida 已提交
101 102
};
```
L
lucifer 已提交
103

z2014z's avatar
z2014z 已提交
104
C++ Code:
L
lucifer 已提交
105 106 107

> C++代码与上面的 JavaScript 代码略有不同:将 carry 是否为 0 的判断放到了 while 循环中

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
```c++
/**
 * 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;
    }
};
```
L
lucifer 已提交
142

z2014z's avatar
z2014z 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
Java Code:

```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;
L
lucifer 已提交
169

z2014z's avatar
z2014z 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
        }
        if (carry > 0) {
            cur.next = new ListNode(carry);
        }
        return dummyHead.next;
    }
}

```

Python Code:

```py
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

```

L
lucifer 已提交
216 217
**复杂度分析**

L
lucifer 已提交
218 219
- 时间复杂度:$O(N)$
- 空间复杂度:$O(1)$
L
lucifer 已提交
220

221 222
## 拓展

L
lucifer 已提交
223 224
通过单链表的定义可以得知,单链表也是递归结构,因此,也可以使用递归的方式来进行 reverse 操作。

L
lucifer 已提交
225
> 由于单链表是线性的,使用递归方式将导致栈的使用也是线性的,当链表长度达到一定程度时,递归可能会导致爆栈,
226

L
lucifer 已提交
227
### 算法
228

L
lucifer 已提交
229
1. 将两个链表的第一个节点值相加,结果转为 0-10 之间的个位数,并设置进位信息
230
2. 将两个链表第一个节点以后的链表做带进位的递归相加
L
lucifer 已提交
231 232 233
3. 将第一步得到的头节点的 next 指向第二步返回的链表

### C++实现
234 235 236 237 238 239 240 241

```C++
// 普通递归
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        return addTwoNumbers(l1, l2, 0);
    }
L
lucifer 已提交
242

243 244 245 246 247 248 249 250 251 252 253
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;
    }
};
L
lucifer 已提交
254
// 类似尾递归
255 256 257 258 259 260 261
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = nullptr;
        addTwoNumbers(head, nullptr, l1, l2, 0);
        return head;
    }
L
lucifer 已提交
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
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);
    }
};
```
L
lucifer 已提交
279 280

**复杂度分析**
L
lucifer 已提交
281 282 283

- 时间复杂度:$O(N)$
- 空间复杂度:$O(N)$,其中 N 的空间是调用栈的开销。
L
lucifer 已提交
284 285 286 287

大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。
大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg)