不积跬步,无以至千里;不积小流,无以成江海。
忘忧跟大家一起,从简单的开始入手,一步一步的刷遍leetcode,今天跟大家聊一下跟链表相关的一个基础算法题。
题目描述
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
- 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
- 输出:7 -> 0 -> 8
- 原因:342 + 465 = 807
问题分析
- 注意进位
- 链表的长度可能不同
- 输出结果也逆序
解题思路
解决这个问题,忘忧最先想到的是,将输入的两个列表转换为整数,计算出结果后,再将结果转换成链表。
但是,又回头看了一下题目要求,并没有约定数的取值范围,当数值较大时会导致溢出,所以排除了这个想法。
又考虑到,不如干脆将链表计算完成后直接存储到链表里边,既节省时间又节省空间。
确定了大致思路之后,开始思考详细的细节。
- 链表第一位相加,得出一个数值,并且记录是否进位
- 下一个节点的两个数与进的位相加,得出该位置的和,并记录进位,重复第二部操作,当链表为在该位置没有数时默认为0,直到链表全都走到尽头位置
- 两个链表均遍历完后,需要判断最后一次计算有无进位,如果有进位的话,在结果的末尾追加1的节点。
具体过程如下图所示
具体代码实现如下:
/**
* 两数之和
* @param l1 链表1
* @param l2 链表2
* @return 和链表
*/
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
boolean flog = false;
//头节点的前置节点,为了后续不判空而引入的额外节点
ListNode head = new ListNode(0);
ListNode tmp = head;
while(l1 != null || l2 != null){
//计算当前位置的和,如果l1在当前位置没有数字,则默认为0,l2同理
//如果进位标示为true,在l1和l2的计算结果之上,再加上进位
int sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + (flog ? 1 : 0);
//进位标示,每次分析结果前都标记为false
flog = false;
//如果和大于10,将进位标示更新为true,sum只保留个位上的数字
if(sum >= 10){
flog = true;
sum %= 10;
}
//用当前的sum初始化一个新节点
ListNode listNode = new ListNode(sum);
//tmp的next指向该节点后,然后将该节点赋值给tmp
tmp.next = listNode;
tmp = listNode;
//l1和l2都后移一位,已经为null的链表不操作
l1 = l1 == null ? null : l1.next;
l2 = l2 == null ? null : l2.next;
}
//如果最后一次运算需要进位,在结果的末尾追加1
if(flog){
tmp.next = new ListNode(1);
}
return head.next;
}
题目进阶
本题目为了降低难度,故意将输入的两个列表设置为了逆序,假如说输入的是正向的数组,这个算法又该如何处理呢?
忘忧给大家提供三个思路,大家可以自己尝试一下,在此忘忧就不做具体的实现了
- 思路一: 偷懒式(既然输入的是正序的链表,那么拿到入参后,将两个链表手动反转一下是不是就可以继续沿用上边解逆序的方法了)
- 思路二: 递归法(先通过递归,找到最后一个节点,处理完最后一个节点后,记录下是否进位,然后回退到倒数第二位……)
- 思路三: 栈(利用栈,当链表有后继节点时,对节点的数据做压栈操作,当后继节点全部处理完了,再逐次弹栈)
结合这个小算法记得顺便复习一下链表的相关知识哦~
结束语:温故而知新,可以为师矣