2. 两数相加
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
分析
链表是倒序存储,相对较容易,只要同时遍历两个链表,将两数相加再加上进位,位数不够以0处理,然后将本次的进位和本次的答案记录下来。
注意最后要处理首位的进位。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyNode = new ListNode(-1);
ListNode a = l1, b = l2, cur = dummyNode;
int carry = 0;
while (a != null || b != null) {
int x = a == null ? 0 : a.val;
int y = b == null ? 0 : b.val;
int tmp = x + y + carry;
carry = tmp / 10;
cur.next = new ListNode(tmp % 10);
cur = cur.next;
if (a != null) a = a.next;
if (b != null) b = b.next;
}
if (carry > 0) {
cur.next = new ListNode(carry % 10);
}
return dummyNode.next;
}
445. 两数相加 II
给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7
分析
这道题与上一题的区别在于链表是正序的,导致链表的遍历方向与数字的加运算相反,我们可以使用栈结构来保存链表的数值,再从栈中取出进行运算。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<Integer> s1 = new Stack<Integer>();
Stack<Integer> s2 = new Stack<Integer>();
while (l1 != null) {
s1.push(l1.val);
l1 = l1.next;
}
while (l2 != null) {
s2.push(l2.val);
l2 = l2.next;
}
int x, y, carry = 0;
ListNode p = null;
ListNode next = null;
while (!s1.isEmpty() || !s2.isEmpty()) {
x = s1.isEmpty() ? 0 : s1.pop();
y = s2.isEmpty() ? 0 : s2.pop();
int tmp = x + y + carry;
next = new ListNode(tmp % 10);
next.next = p;
p = next;
carry = tmp / 10;
}
if (carry > 0) {
next = new ListNode(1);
next.next = p;
}
return next;
}
另一种递归的思路,通过判断两个链表的长度,得到其偏移量,在偏移量不为0时,就只取长度更长的链表的节点,每次递归偏移量都减一,当减为0时,说明两个链表的节点都可以参与运算了,直到其中两个同时遍历完成时递归进行返回。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
int size1 = getCount(l1);
int size2 = getCount(l2);
ListNode head = new ListNode(1);
// 确保长度 l1 大于 l2
head.next = size1 > size2 ? helper(l1, l2, size1 - size2) : helper(l2, l1, size2 - size1);
// 处理进位
if (head.next.val > 9) {
head.next.val = head.next.val % 10;
return head;
}
return head.next;
}
public int getCount(ListNode l) {
int count = 0;
while (l != null) {
l = l.next;
count ++;
}
return count;
}
// offset是 l1 和 l2 之间的长度偏移量
public ListNode helper(ListNode l1, ListNode l2, int offset) {
if (l1 == null) return null;
// 如果偏移量等于0,说明可以将两者的节点加起来,否则就只加长度更长的l1
ListNode result = offset == 0 ? new ListNode(l1.val + l2.val) : new ListNode(l1.val);
// 确保递归的退出
ListNode next = offset == 0 ? helper(l1.next, l2.next, 0) : helper(l1.next, l2, offset - 1);
// 处理进位
if (next != null && next.val > 9) {
result.val += 1;
next.val = next.val % 10;
}
// 连接节点
result.next = next;
return result;
}
需要注意的就是,每次递归都需要将当前节点连接下一个节点,然后返回当前节点,层层递归最后就可以得到答案。
66. 加一
给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
分析
题目很简单,但要注意几点:
- 计算顺序应从数组的最后一个数字开始
- 加一在操作如果放在循环里面,只在第一次的时候加一
- 确定最后的数组大小,数字加一后会不会进位
public int[] plusOne(int[] digits) {
LinkedList<Integer> list = new LinkedList<Integer>();
int carry = 0;
int tmp = digits[digits.length - 1] + 1 + carry;
list.addFirst(tmp % 10);
carry = tmp / 10;
for (int i = digits.length - 2 ; i >= 0 ; i --) {
tmp = digits[i] + carry;
list.addFirst(tmp % 10);
carry = tmp / 10;
}
if (carry > 0) {
list.addFirst(carry % 10);
}
// 确定终止的数组大小
int[] arr = new int[list.size()];
for (int i = 0 ; i < list.size() ; i ++) {
arr[i] = list.get(i);
}
return arr;
}
如果嫌上面的思路做这道题太复杂,下面有一个更加聪明的解法。
public int[] plusOne(int[] digits) {
int len = digits.length;
for (int i = len - 1; i > -1; i--) {
if (digits[i] == 9 ) { // 如果是9,改为0
digits[i] = 0;
} else { // 一旦数字不是9,则不会进位,直接返回即可
digits[i] += 1;
return digits;
}
}
// 如果数组元素都是9,创建一个新的长度+1的数组,正好进位
int[] newArray = new int[len + 1];
newArray[0] = 1;
return newArray;
}
67. 二进制求和 & 415. 字符串相加
分析
这两道题,只是进制的不同,解法是相同的。
public String addBinary(String a, String b) {
StringBuilder sb = new StringBuilder();
int m = a.length();
int n = b.length();
int i = m - 1, j = n - 1, carry = 0;
while (i >= 0 || j >= 0) {
int x = m - i < n - j ? 0 : Integer.valueOf(a.charAt(i) - '0');
int y = m - i > n - j ? 0 : Integer.valueOf(b.charAt(j) - '0');
int tmp = x + y + carry;
sb.append(tmp % 2); // 十进制改为 10 即可
carry = tmp / 2;
if (i == 0 && j == 0)
break;
if (i != 0) i --;
if (j != 0) j --;
}
if (carry > 0) {
sb.append(carry);
}
return sb.reverse().toString();
}
如果文章里有说得不对的地方请前辈多多指正~ 与君共勉~