两数相加
题目:
我的解法:
/**
* @Description TODO
* @Param [l1, l2]
* @return arrays.addTwoNumbers.ListNode
* @date 2020/7/14 17:21
* @author huanl
*
* 思路:先把他全部拿出来,拿出来的时候用字符串的append函数连接起来,后面再用reverse去反转.在把他们相加的到的
* 数转换为StringBuilder类型,然后反转,然后在把他变成ListNode类型.
*
* 问题: 为什么要全部拿出来? 我们正常时候做加法,不就是从低位做起,然后有进位就记录一下,下一次添加上.现在链表他给我们就是已经弄好了的低位,
* 直接从低位开始相加就好了.
* 直接一个一个取然后加的话怎么判断他本身到底有没有值? 噢,直接判断这个节点是不是为null
* 加到最后可能最后一次的进位记录为一,怎么处理? 再在后面多加一个节点,将这个一记录上去.
*/
public ListNode addTwoNumber(ListNode l1, ListNode l2) {
//用StringBuilder取出l1和l2里面所有的
StringBuilder num1 = new StringBuilder();
StringBuilder num2 = new StringBuilder();
num1.append(l1.val);
num2.append(l2.val);
while (l1.next != null) {
num1.append(l1.next.val);
l1 = l1.next;
}
while (l2.next != null) {
num2.append(l2.next.val);
l2 = l2.next;
}
//反转为正常的顺序
num1 = num1.reverse();
num2 = num2.reverse();
//将两个字符串相加 (因为直接一次的话会超出int或者long的长度) 其实最好就是将他们逆转过来,这样才符合我们日常的加法
int[] nums = new int[(Math.max(num1.length(), num2.length())) + 1];
for (int i = 1; i <= (Math.max(num1.length(), num2.length())); i++) {
if (i>num1.length()){
nums[nums.length-i] = Integer.parseInt(String.valueOf(num2.charAt(num2.length()-i)));
}else if(i>num2.length()){
nums[nums.length-i] = Integer.parseInt(String.valueOf(num1.charAt(num1.length()-i)));
}else {
nums[nums.length-i] = Integer.parseInt(String.valueOf(num1.charAt(num1.length()-i))) + Integer.parseInt(String.valueOf(num2.charAt(num2.length()-i)));
}
}
//处理进位信息
for (int i = nums.length - 1; i > 0; i--) {
nums[i-1] = nums[i-1] + nums[i] / 10;
nums[i] = nums[i] % 10;
}
//因为两数相加可能会多一位,也就是第一位可能为1,可能为0,这里就先不处理第一位,直接从第二位开始处理
StringBuilder num = new StringBuilder();
for (int i = 1; i < nums.length ; i++) {
num.append(nums[i]);
}
//处理第一位,如果为一,则直接插入最前面
if (nums[0] != 0) {
num.insert(0,nums[0]);
}
num = num.reverse();
//将字符串类型的转化为ListNode类型
ListNode ret = new ListNode(num.charAt(0) - 48);
ListNode jilu = ret;
for (int i = 1; i < num.length(); i++) {
jilu.next = new ListNode(num.charAt(i) - 48);
jilu = jilu.next;
}
return ret;
}
问题:
-
为什么要先全部取出来? 有什么好处? 还是不能直接一个一个操作?
-
正是因为想要全部取出来,然后StringBuilder类型拼接快,然后StringBuilder又有一个反转函数,所以去用了StringBuilder,然后反转之后,本来想要用Integer.parseInt()转化为int然后相加,但是发现这个太容易直接超出范围,所以不得不直接用StringBuilder类型的直接相加,并且还用了反转之后的来相加,这就导致了相加函数复杂,并且相加之后,还需要处理进位信息,处理有没有多了一位的信息.
-
总结:
1. 第一步的思路就问题,导致后续麻烦不断的到来,那为什么我会想取直接把他全部取出来呢? 没有取过多的考虑全部取出来有什么麻烦,就是简单就直接去取出来 2. 能够去写出不定长字符串的加法,这个值得鼓励.
优解:
/**
* @Description TODO
* @Param [l1, l2]
* @return arrays.addTwoNumbers.ListNode
* @date 2020/7/14 17:08
* @author huanl
*/
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
if (carry > 0) {
curr.next = new ListNode(carry);
}
return dummyHead.next;
}
优点:
1. 一个一个取,取出来直接进行操作,该进位的直接将进位符标志为1.
为什么要一个一个取?
因为他给我们的就是符合加法计算规律的,也就是先从低位算起,有进位加到高位,并且链表取值的方向也正好是从低位到高位,各种条件都非常适合直接加.
2. 注意细节:
a. 先建造一个头节点,这个头节点里面的值不和任何有关,那为什么要建一个这样的头节点呢? 因为没有这个头节点,第一次加法的时候就没有链表去操作,那么就势必需要把第一次提出来先做,这样就感觉有点不统一了.
b. 用另外一个指针去操作,因为你需要返回这个链表,那么这个链表的头指针你必须保存一个,不然你就找不到这个指针了.
c. 注意判断此时值到底取val还是0,判断条件是什么? 直接判断这个节点是不是空节点,因为我们操作的就是此节点,不是next节点.
d. 注意可能最后一次加法时,进位为一,那么则需要在给他最后再加上一个节点,例如999+1就可能得到4位,这样就需要多加一个节点.
经验
- 先想一下怎么做,然后确定觉得这样做可以之后,把详细的思路写一遍,想一想为什么每一步需要这样做?和实际的相符吗?
节点.
d. 注意可能最后一次加法时,进位为一,那么则需要在给他最后再加上一个节点,例如999+1就可能得到4位,这样就需要多加一个节点.
## 经验
1. 先想一下怎么做,然后确定觉得这样做可以之后,把详细的思路写一遍,想一想为什么每一步需要这样做?和实际的相符吗?
2. 不定长字符串的加法,每位保存一个值的算法.