|
| 1 | +# LeetCode 第 445 号问题:两数相加 II |
| 2 | + |
| 3 | +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。 |
| 4 | +> |
| 5 | +> 同步个人博客:https://www.zhangxiaoshuai.fun |
| 6 | + |
| 7 | + |
| 8 | +```txt |
| 9 | +难度:medium 目前通过率:57.2% |
| 10 | +题目描述: |
| 11 | + 给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。 |
| 12 | + 它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 |
| 13 | + 你可以假设除了数字0之外,这两个数字都不会以零开头。 |
| 14 | + |
| 15 | +示例: |
| 16 | + 输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) |
| 17 | + 输出: 7 -> 8 -> 0 -> 7 |
| 18 | +``` |
| 19 | + |
| 20 | +<!-- more --> |
| 21 | + |
| 22 | +显然,给定的两个"数字"都是**从左到右按照高位到低位的顺序**排列的,按照习惯我们需要将链表反转之后然后相加,再将结果链表反转之后返回。 |
| 23 | + |
| 24 | +那么顺序的问题解决了,还存在一个问题: |
| 25 | + |
| 26 | +**两个一位数相加是有可能产生一个两位数的,而我们的节点中是不能存储一个两位数字的,如果产生进位那我们需要将进位保存下来,然后将进位加到后面的两个数字相加的结果中。** |
| 27 | + |
| 28 | +**下面是GIF图解:** |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +**那么我们先来试试写出这个版本的代码:** |
| 33 | + |
| 34 | +因为在整个计算中,链表反转的这个功能用到了三次,那我们可以将这个功能单独的抽取出来写一个方法。(方法比较简单,直接附上代码,不再赘述) |
| 35 | + |
| 36 | +```java |
| 37 | +//链表反转 |
| 38 | +private ListNode reverseListNode(ListNode head){ |
| 39 | + if(head == null) return null; |
| 40 | + ListNode prev = null; |
| 41 | + while (head != null) { |
| 42 | + ListNode next = head.next; |
| 43 | + head.next = prev; |
| 44 | + prev = head; |
| 45 | + head = next; |
| 46 | + } |
| 47 | + return prev; |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +要返回两条链表相加的结果,我们需要新的链表来存储这个结果,dummy节点的值随意,因为我们最终返回的是连接在它后面的聊链表。 |
| 52 | + |
| 53 | +我们需要初始化**addOne**(代表进位)为0 |
| 54 | + |
| 55 | +```java |
| 56 | +//两条链表中的各个节点数字相加 |
| 57 | +private ListNode add(ListNode l1, ListNode l2){ |
| 58 | + if (l1 == null && l2 == null) return null; |
| 59 | + ListNode dummy = new ListNode(0); |
| 60 | + ListNode temp = dummy; |
| 61 | + int addOne = 0; |
| 62 | + while (l1 != null || l2 != null || addOne != 0) { |
| 63 | + int val1 = l1 == null ? 0 : l1.val; |
| 64 | + int val2 = l2 == null ? 0 : l2.val; |
| 65 | + int sum = val1 + val2 + addOne; |
| 66 | + temp.next = new ListNode(sum%10); |
| 67 | + temp = temp.next; |
| 68 | + addOne = sum / 10; |
| 69 | + if (l1 != null) l1 = l1.next; |
| 70 | + if (l2 != null) l2 = l2.next; |
| 71 | + } |
| 72 | + return dummy.next; |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +ok,那我们现在只需要在指定的方法中调用这两个方法并传递相应的参数即可得到我们想要的。 |
| 77 | + |
| 78 | +```java |
| 79 | +public static ListNode addTwoNumbers(ListNode l1, ListNode l2){ |
| 80 | + ListNode node1 = reverseListNode(l1); |
| 81 | + ListNode node2 = reverseListNode(l2); |
| 82 | + return reverseListNode(add(node1,node2)); |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +如果你觉得上面的解法太过简单,那一起来看看进阶版本吧: |
| 87 | + |
| 88 | +**如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。** |
| 89 | +那我们上面的使用的"反转链表"的方法就无法再次使用了。 |
| 90 | +首先我们应该明确"反转链表"方法的出现是为了解决链表中"数字"的顺序问题,那么如果要得到正确的数字顺序只有这一中方法吗? |
| 91 | +我们还有可以依靠的数据结构:**栈**。栈的特点就是"**先进后出**" |
| 92 | + |
| 93 | +**算法思路:**先将两条链表的各个数字分别压入两个栈中,然后每次弹出两个栈顶的数字进行相加,第一次相加的结点指向null结点,每次更新头节点,后面的结点依次指向新链表的头节点,那么最终我们可以得到与上面解法相同的结果。 |
| 94 | + |
| 95 | +**解法2图解:** |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +**代码:** |
| 100 | + |
| 101 | +```java |
| 102 | +public static ListNode addTwoNumbers(ListNode l1, ListNode l2){ |
| 103 | + Stack<Integer> s1 = new Stack<>(); |
| 104 | + Stack<Integer> s2 = new Stack<>(); |
| 105 | + while (l1 != null) { |
| 106 | + s1.push(l1.val); |
| 107 | + l1 = l1.next; |
| 108 | + } |
| 109 | + while (l2 != null) { |
| 110 | + s2.push(l2.val); |
| 111 | + l2 = l2.next; |
| 112 | + } |
| 113 | + int addOne = 0;//进位 |
| 114 | + ListNode head = null; |
| 115 | + while (!s1.isEmpty() || !s2.isEmpty() || addOne != 0) { |
| 116 | + int sum = addOne; |
| 117 | + sum += s1.isEmpty() ? 0 : s1.pop(); |
| 118 | + sum += s2.isEmpty() ? 0 : s2.pop(); |
| 119 | + ListNode temp = new ListNode(sum%10); |
| 120 | + temp.next = head; |
| 121 | + head = temp; |
| 122 | + addOne = sum/10; |
| 123 | + } |
| 124 | + return head; |
| 125 | + } |
| 126 | +``` |
| 127 | + |
| 128 | + |
0 commit comments