栈题目:两数相加 II

题目

标题和出处

标题:两数相加 II

出处:445. 两数相加 II

难度

4 级

题目描述

要求

给你两个非空的链表,表示两个非负的整数。数字最高位位于链表开始位置,并且每个结点只存储一位数字。将这两数相加会返回一个新的链表。将两个数相加,并返回一个表示和的链表。

你可以假设除了数字 0 \texttt{0} 0 之外,这两个数字都不会以 0 \texttt{0} 0 开头。

示例

示例 1:

示例 1

输入: l1   =   [7,2,4,3],   l2   =   [5,6,4] \texttt{l1 = [7,2,4,3], l2 = [5,6,4]} l1 = [7,2,4,3], l2 = [5,6,4]
输出: [7,8,0,7] \texttt{[7,8,0,7]} [7,8,0,7]

示例 2:

输入: l1   =   [2,4,3],   l2   =   [5,6,4] \texttt{l1 = [2,4,3], l2 = [5,6,4]} l1 = [2,4,3], l2 = [5,6,4]
输出: [8,0,7] \texttt{[8,0,7]} [8,0,7]

示例 3:

输入: l1   =   [0],   l2   =   [0] \texttt{l1 = [0], l2 = [0]} l1 = [0], l2 = [0]
输出: [0] \texttt{[0]} [0]

数据范围

  • 每个链表中的结点数在范围 [1,   100] \texttt{[1, 100]} [1, 100]
  • 0 ≤ Node.val ≤ 9 \texttt{0} \le \texttt{Node.val} \le \texttt{9} 0Node.val9
  • 题目数据保证列表表示的数字不含前导零

进阶

你能否在不反转输入链表的情况下解决?

解法

思路和算法

这道题和「两数相加」的区别在于数位在链表中的存储顺序,这道题中,链表的头结点表示最高位。由于计算加法需要按照从最低位到最高位的顺序计算,因此需要对链表的结点顺序反转。

可以使用「反转链表」的解法将两个输入链表反转,然后使用「两数相加」的解法对两个反转后的输入链表计算和,最后将表示和的链表反转,即可得到答案。

在不反转输入链表的情况下,必须借助其他数据结构。由于栈的特点是后进先出,因此可以使用栈实现反转链表的结点顺序,同时不改变原始链表的结构。

使用两个栈分别存储两个链表的结点。对于每个链表,依次遍历链表中的每个结点并将结点入栈,遍历结束后,栈底元素为链表的头结点,即最高位,栈顶元素为链表的尾结点,即最低位。

将两个链表的全部结点入栈以后,即可计算两数相加的结果。存储结果的栈称为输出栈。由于两个栈的栈顶元素都是最低位,因此每次同时从两个栈分别弹出一个结点,这两个结点对应的数位是相同的,即都是个位、都是十位、都是百位等。相同数位的数字可以直接相加,相加过程中会产生进位。

计算过程可分为三个阶段:两个栈都有结点、只有一个栈有结点、只有进位部分。

第一阶段,两个栈都有结点。记两个栈弹出的结点分别为 node 1 \textit{node}_1 node1 node 2 \textit{node}_2 node2,进位值为 carry \textit{carry} carry,则两个结点对应的和结点的值为 ( node 1 + node 2 + carry )   m o d   10 (\textit{node}_1 + \textit{node}_2 + \textit{carry}) \bmod 10 (node1+node2+carry)mod10,进位值为 ⌊ node 1 . val + node 2 . val + carry 10 ⌋ \Big\lfloor \dfrac{\textit{node}_1.\textit{val} + \textit{node}_2.\textit{val} + \textit{carry}}{10} \Big\rfloor 10node1.val+node2.val+carry,将进位值更新到 carry \textit{carry} carry

第二阶段,只有一个栈有结点。如果两个链表的长度不同,则两个栈的元素个数也不同,其中的一个栈会先变成空,然后只剩下一个栈还有结点。此时只需要考虑非空的栈,继续将结点弹出和计算和。

第三阶段,只有进位部分。当两个栈都变成空时,如果 carry \textit{carry} carry 大于 0 0 0,则需要将进位值继续填入表示和的链表中,直到进位值变成 0 0 0

具体实现方面,使用一个结果栈存储结果,在计算结果链表时,将每个结点依次加入结果栈,则结果栈的栈底元素为结果链表的尾结点,即最低位,栈顶元素为结果链表的头结点,即最高位。用 nodeSum \textit{nodeSum} nodeSum 表示加入结果栈的结点,在将 nodeSum \textit{nodeSum} nodeSum 加入结果栈之前,如果结果栈不为空,则获得结果栈的栈顶结点,并将 nodeSum \textit{nodeSum} nodeSum next \textit{next} next 指针指向栈顶结点,创建结果栈内的各结点的链接关系,从而得到结果链表。

计算结束之后,结果栈的栈顶结点即为结果链表的头结点。

下图为计算 42 + 965 42 + 965 42+965 的例子。存储输入结点的两个栈的栈顶在右边,栈底在左边,存储结果结点的栈的栈顶在左边,栈底在右边。数位相同的结点在图中对齐,结点之间的连接关系在图中省略。

在这里插入图片描述

代码

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Deque<ListNode> stack1 = new ArrayDeque<ListNode>();
        Deque<ListNode> stack2 = new ArrayDeque<ListNode>();
        ListNode temp1 = l1, temp2 = l2;
        while (temp1 != null) {
            stack1.push(temp1);
            temp1 = temp1.next;
        }
        while (temp2 != null) {
            stack2.push(temp2);
            temp2 = temp2.next;
        }
        Deque<ListNode> stackSum = new ArrayDeque<ListNode>();
        int carry = 0;
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            ListNode node1 = stack1.pop(), node2 = stack2.pop();
            int val = node1.val + node2.val + carry;
            ListNode nodeSum = new ListNode(val % 10);
            if (!stackSum.isEmpty()) {
                nodeSum.next = stackSum.peek();
            }
            stackSum.push(nodeSum);
            carry = val / 10;
        }
        while (!stack1.isEmpty()) {
            ListNode node1 = stack1.pop();
            int val = node1.val + carry;
            ListNode nodeSum = new ListNode(val % 10);
            if (!stackSum.isEmpty()) {
                nodeSum.next = stackSum.peek();
            }
            stackSum.push(nodeSum);
            carry = val / 10;
        }
        while (!stack2.isEmpty()) {
            ListNode node2 = stack2.pop();
            int val = node2.val + carry;
            ListNode nodeSum = new ListNode(val % 10);
            if (!stackSum.isEmpty()) {
                nodeSum.next = stackSum.peek();
            }
            stackSum.push(nodeSum);
            carry = val / 10;
        }
        while (carry != 0) {
            ListNode curNode = new ListNode(carry % 10);
            if (!stackSum.isEmpty()) {
                curNode.next = stackSum.peek();
            }
            stackSum.push(curNode);
            carry /= 10;
        }
        return stackSum.peek();
    }
}

复杂度分析

  • 时间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n 分别是两个链表的长度。需要分别将两个链表中的全部结点入栈,时间复杂度是 O ( m + n ) O(m + n) O(m+n),计算两数相加的结果时需要同时反向遍历两个链表,时间复杂度是 O ( max ⁡ ( m , n ) ) O(\max(m, n)) O(max(m,n)),因此总时间复杂度是 O ( m + n ) O(m + n) O(m+n)

  • 空间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m n n n 分别是两个链表的长度。空间复杂度主要取决于存储输入链表和结果链表的结点的栈空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值