Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
今天我们来看看第二题:两数相加
两数相加
题目描述:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。(你可以假设除了数字 0 之外,这两个数都不会以 0 开头。)
解题思路
在这道题中,首先,提到了逆序存储,我们在做加法运算时,是从个位数开始的,所以在这里我们不需要倒序处理数据存储,但我们要处理的是加法运算中的逢十进一位,也就是进位处理。 其次,这题考察了我们对链表双指针的操作。
需要注意的就是:
- 获取两个链表对应的长度
- 对齐相加考虑进位
- 在较短的链表末尾补零
同时在做链表题时,有个技巧就是使用dummy(虚拟头节点)。好处是,不会影响起始节点的操作,如果不理解这个技巧的话,在之后的链表题中它的作用也会不言而喻。
在这题中,我想到的是使用while循环解法,或者递归解法去实现。
while循环解法
首先创建一个虚拟的头节点dummy,我们把需要操作的链表都放在这个节点下;
然后我们需要记录一个进位数,用来在后面运算。
再然后我们开启一个 while循环,满足条件就是 两个链表都为空,且没有进位时跳出循环。
在循环内,处理两个链表中值的加法运算,逢十进一,进位给下一次运算的结果叠加。使用运算后的结果生成一个节点,并将操作的指针切换到下一个节点,进行下一个循环
循环结束后,返回虚拟的头节点下的下一个节点即dummy.next。
class Solution {
func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
var carry = 0
let dummy = ListNode(-1)
var newNode = dummy
var n1: ListNode? = l1
var n2: ListNode? = l2
while n1 != nil || n2 != nil || carry > 0 {
var val = carry
if let node1 = n1 {
val += node1.val
n1 = node1.next
}
if let node2 = n2 {
val += node2.val
n2 = node2.next
}
carry = val / 10
val = val % 10
newNode.next = ListNode(val)
newNode = newNode.next!
}
return dummy.next
}
}
递归解法
递归在整体思路上应该也是一样,只是方式不同,如果把运算过程拆成每一位运算,那么使用递归,思路应该就会很清晰。我们的递归过程其实就是运算的过程,只是需要做好运算结束的判断条件即可。
class Solution {
func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
let dummy = ListNode(-1)
addTwoNumber(l1, l2, carry: 0, newNode: dummy)
return dummy.next
}
func addTwoNumber(_ l1: ListNode?, _ l2: ListNode?, carry: Int, newNode: ListNode)-> ListNode? {
if l1 == nil, l2 == nil, carry == 0 {
return nil
}
var carry = carry
var n1: ListNode? = l1
var n2: ListNode? = l2
var val = carry
// 节点1
if let node1 = n1 {
val += node1.val
n1 = node1.next
}
// 节点2
if let node2 = n2 {
val += node2.val
n2 = node2.next
}
// 这里可以通过整除 或者 减法来处理进位
carry = val / 10
val = val % 10
let nextNode = ListNode(val)
newNode.next = nextNode
// 返回下一个节点
return addTwoNumber(n1, n2, carry: carry, newNode: nextNode)
}
}
总结
针对链表的操作,我们可以优先使用虚拟节点。如果不使用 dummy 虚拟节点,代码会复杂很多,而有了 dummy 节点这个占位符,可以很大程度避免空指针的情况,同时也可以降低代码的复杂性。
针对两数相加,位数运算其实没什么好说的,但是如果顺序是正序的话,我们处理起来就会比倒序要麻烦一些了。