This commit is contained in:
chefyuan 2021-03-19 15:07:33 +08:00
parent b81ee61e58
commit dfdec0d511
10 changed files with 838 additions and 0 deletions

View File

@ -0,0 +1,125 @@
#### leetcode 234. 回文链表
请判断一个链表是否为回文链表。
示例 1:
```
输入: 1->2
输出: false
```
示例 2:
```java
输入: 1->2->2->1
输出: true
```
题目解析:
题目理解起来很简单判断是否为回文如果单纯判断一个字符串或者数组是不是回文很容易。因为数组查询元素的时间复杂度为O(1)但是链表的查询时间复杂度为O(n),而且题目中的链表为单链表,指针只能后移不能前移。所以我们判断起来会比较困难。
巧用数组法:
我们首先将链表的所有元素都保存在数组中,然后再利用双指针遍历数组,进而来判断是否为回文。这个方法很容易理解,而且代码实现也比较简单。
```java
class Solution {
public boolean isPalindrome(ListNode head) {
//这里需要用动态数组,因为我们不知道链表的长度
List<Integer> arr = new ArrayList<Integer>();
ListNode copynode = head;
//将链表的值复制到数组中
while (copynode != null) {
arr.add(copynode.val);
copynode = copynode.next;
}
// 双指针遍历数组
int back = 0;
int pro = arr.size() - 1;
while (back < pro) {
//判断两个指针的值是否相等
if (!arr.get(pro).equals(arr.get(back))) {
return false;
}
//移动指针
back++;
pro--;
}
return true;
}
}
```
这个方法可以直接通过,但是这个方法需要辅助数组,那我们还有其他更好的方法吗?
双指针翻转链表法
在上个题目中我们知道了如何找到链表的中间节点,那我们可以在找到中间节点之后,对后半部分进行翻转,翻转之后,重新遍历前半部分和后半部分进行判断是否为回文。
动图解析:
![翻转链表部分](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/翻转链表部分.1v2ncl72ligw.gif)
```java
class Solution {
public boolean isPalindrome(ListNode head) {
if (head==null || head.next==null) {
return true;
}
//找到中间节点,也就是翻转的头节点,这个在昨天的题目中讲到
//但是今天和昨天有一些不一样的地方就是,如果有两个中间节点返回第一个,昨天的题目是第二个
ListNode midenode = searchmidnode(head);
//原地翻转链表,需要两个辅助指针。这个也是面试题目,大家可以做一下
//这里我们用的是midnode.next需要注意因为我们找到的是中点但是我们翻转的是后半部分
ListNode backhalf = reverse(midenode.next);
//遍历两部分链表,判断值是否相等
ListNode p1 = head;
ListNode p2 = backhalf;
while (p2 != null) {
if (p1.val != p2.val) {
return false;
}
p1 = p1.next;
p2 = p2.next;
}
// 还原链表并返回结果,这一步是需要注意的,我们不可以破坏初始结构,我们只是判断是否为回文,
//当然如果没有这一步也是可以AC但是面试的时候题目要求可能会有这一条。
midenode.next = reverse(backhalf);
return true;
}
//找到中间的部分
public ListNode searchmidnode (ListNode head) {
ListNode fast = new ListNode(-1);
ListNode slow = new ListNode(-1);
fast = head;
slow = head;
//找到中点
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
//翻转链表
public ListNode reverse (ListNode slow) {
ListNode low = null;
ListNode temp = null;
//翻转链表
while (slow != null) {
temp = slow.next;
slow.next = low;
low = slow;
slow = temp;
}
return low;
}
}
```

View File

@ -0,0 +1,149 @@
#### leetcode 142. 环形链表 II
题目描述:
今天给大家介绍比较有特点的题目,也是一个特别经典的题目,判断链表中有没有环,并返回环的入口。
我们可以先做一下这个题目,就是如何判断链表中是否有环呢?下图则为链表存在环的情况。
![image-20201027175552961](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201027175552961.63sz69rbes00.png)
判断链表是否有环是很简单的一个问题,我们只需利用之前的快慢指针即可,大家想一下,指针只要进入环内就只能在环中循环,那么我们可以利用快慢指针,虽然慢指针的速度小于快指针但是,总会进入环中,当两个指针都处于环中时,因为移动速度不同,两个指针必会相遇。
我们可以这样假设,两个孩子在操场顺时针跑步,一个跑的快,一个跑的慢,跑的快的那个孩子总会追上跑的慢的孩子。
环形链表:
```java
public class Solution {
public boolean hasCycle(ListNode head) {
//特殊情况,无节点或只有一个节点的情况
if(head == null || head.next == null){
return false;
}
//设置快慢指针
ListNode pro = head.next;
ListNode last = head;
//循环条件
while( pro != null && pro.next!=null){
pro=pro.next.next;
last=last.next;
//两指针相遇
if(pro == last){
return true;
}
}
//循环结束,指针没有相遇,说明没有环。相当于快指针遍历了一遍链表
return false;
}
}
```
判断链表是不是含有环很简单,但是我们想找到环的入口可能就没有那么容易了。(入口则为下图绿色节点)
然后我们返回的则为绿色节点的索引则返回2。
![image-20201027180921770](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201027180921770.21fh8pt3cuv4.png)
### HashSet
我们可以利用HashSet来做之前的文章说过HashSet是一个不允许有重复元素的集合。所以我们通过HashSet来保存链表节点对链表进行遍历如果链表不存在环则每个节点都会被存入环中但是当链表中存在环时则会发重复存储链表节点的情况所以当我们发现HashSet中含有某节点时说明该节点为环的入口返回即可。
下图中,存储顺序为 0123456**2 **因为2已经存在则返回。
![image-20201027182649669](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201027182649669.2g8gq4ik6xs0.png)
```java
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return head;
}
if (head.next == null) {
return head.next;
}
//创建新的HashSet,用于保存节点
HashSet<ListNode> hash = new HashSet<ListNode>();
//遍历链表
while (head != null) {
//判断哈希表中是否含有某节点,没有则保存,含有则返回该节点
if (hash.contains(head)) {
return head;
}
//不含有,则进行保存,并移动指针
hash.add(head);
head = head.next;
}
return head;
}
}
```
### 快慢指针
这个方法是比较巧妙的方法,但是不容易想到,也不太容易理解,利用快慢指针判断是否有环很容易,但是判断环的入口就没有那么容易,之前说过快慢指针肯定会在环内相遇,见下图。
![image-20201027184755943](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201027184755943.3edot8s2xi60.png)
上图黄色节点为快慢指针相遇的节点,此时
快指针走的距离:**a+(b+c)n+b**
很容易理解b+c为环的长度a为直线距离b为绕了n圈之后又走了一段距离才相遇所以相遇时走的总路程为a+(b+c)n+b合并同类项得a+(n+1)b+nc。
慢指针走的距离:**a+(b+c)m+b**,m代表圈数。
然后我们设快指针得速度是慢指针的2倍,含义为相同时间内快指针走过的距离是慢指针的2倍。
**a+(n+1)b+nc=2[a+(m+1)b+mc]**整理得**a+b=(n-2m)(b+c)**那么我们可以从这个等式上面发现什么呢?**b+c**
为一圈的长度。也就是说a+b等于n-2m个环的长度。为了便于理解我们看一种特殊情况当n-2m等于1那么a+b=b+c整理得a=c此时我们只需重新释放两个指针一个从head释放一个从相遇点释放速度相同因为a=c所以他俩必会在环入口处相遇则求得入口节点索引。
算法流程:
1.设置快慢指针快指针速度为慢指针的2倍
2.找出相遇点
3.在head处和相遇点同时释放相同速度且速度为1的指针两指针必会在环入口处相遇
![环形链表2](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/环形链表2.elwu1pw2lw0.gif)
```java
public class Solution {
public ListNode detectCycle(ListNode head) {
//快慢指针
ListNode fast = head;
ListNode low = head;
//设置循环条件
while (fast != null && fast.next != null) {
fast = fast.next.next;
low = low.next;
//相遇
if (fast == low) {
//设置一个新的指针从头节点出发慢指针速度为1所以可以使用慢指针从相遇点出发
ListNode newnode = head;
while (newnode != low) {
low = low.next;
newnode = newnode.next;
}
//在环入口相遇
return low;
}
}
return null;
}
}
```

View File

@ -0,0 +1,64 @@
#### 82. 删除排序链表中的重复元素 II
题目描述
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中没有重复出现的数字。
示例 1:
```java
输入: 1->2->3->3->4->4->5
输出: 1->2->5
```
示例 2:
```java
输入: 1->1->1->2->3
输出: 2->3
```
> 注意这里会将重复的值全部删除1123最后只会保留23。
这道题目还是很简单的,更多的是考察大家的代码完整性,删除节点也是题库中的一类题目,我们可以可以通过这个题目举一反三。去完成其他删除阶段的题目。
链表的题目建议大家能有指针实现还是尽量用指针实现,很多链表题目都可以利用辅助空间实现,我们也可以用,学会了那种方法的同时应该再想一下可不可以利用指针来完成。下面我们来思考一下这个题目如何用指针实现吧!
做题思路:
这个题目也是利用我们的双指针思想,一个走在前面,一个在后面紧跟,前面的指针就好比是侦察兵,当发现重复节点时,后面指针停止移动,侦察兵继续移动,直到移动完重复节点,然后将该节点赋值给后节点。思路是不是很简单啊,那么我们来看一下动图模拟吧。
注:这里为了表达更直观,所以仅显示了该链表中存在的节点
![删除重复节点2](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/删除重复节点2.3btmii5cgxa0.gif)
```java
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null||head.next==null){
return head;
}
ListNode pre = head;
ListNode low = new ListNode(0);
low.next = pre;
ListNode ret = new ListNode(-1);
ret = low;
while(pre != null && pre.next != null) {
if (pre.val == pre.next.val) {
while (pre != null && pre.next != null && pre.val == pre.next.val) {
pre = pre.next;
}
pre = pre.next;
low.next = pre;
}
else{
pre = pre.next;
low = low.next;
}
}
return ret.next;
}
}
```

View File

@ -0,0 +1,151 @@
# 链表详解
阅读完本文你会有以下收获
1.知道什么是链表?
2.了解链表的几种类型。
3.了解链表如何构造。
4.链表的存储方式
5.如何遍历链表
6.了解链表的操作。
7.知道链表和数组的不同点
8.掌握链表的经典题目。
### 链表的定义:
> 定义链表是一种递归的数据结构他或者为空null或者是指向一个结点node的引用该结点含有一个泛型的元素和一个指向另一条链表的引用。
我们来对其解读一下链表是一种常见且基础的数据结构是一种线性表但是他不是按线性顺序存取数据而是在每一个节点里存到下一个节点的地址。我们可以这样理解链表是通过指针串联在一起的线性结构每一个链表结点由两部分组成数据域及指针域链表的最后一个结点指向null。也就是我们所说的空指针。
### 链表的几种类型
我们先来看一下链表的可视化表示方法,以便更好的对其理解。
- 用长方形表示对象
- 将实例变量的值写在长方形中;
- 用指向被引用对象的箭头表示引用关系。
#### 单链表
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。
我们通过上面说到的可视化表示方法,构造单链表的可视化模型,如图所示。
![image-20201101143220993](E:\Typora笔记\CSDN\leetcode通关笔记\静态图\单链表.png)
#### 双向链表
上面提到了单链表的节点只能指向节点的下一个节点。而双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接,所以双链表既能向前查询也可以向后查询。
![双链表](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/双链表.3cw4hra1g3q0.png)
####
还有一个常用的链表则为循环单链表则单链表尾部的指针指向头节点。例如在leetcode61旋转链表中我们就是先将链表闭合成环找到新的打开位置并定义新的表头和表尾。
### 构造链表
java是面向对象语言实现链表很容易。我们首先用一个嵌套类来定义节点的抽象数据类型
```java
private class Node {
Item item;
Node next;
}
```
现在我们需要构造一条含有one,two,three的链表我们首先为每个元素创造一个节点
```java
Node first = new Node();
Node second = new Node();
Node third = new Node();
```
将每个节点的item域设为所需的值
```java
first.item = "one";
second.item = "two";
third.item = "three";
```
然后我们设置next域来构造链表
```java
first.next = second;
second.next = third;
```
此时third的next仍为null即被初始化的值。
### 链表的存储方式
我们知道了如何构造链表,我们再来说一下链表的存储方式。
我们都知道数组在内存中是连续分布的,但是链表在内存不是连续分配的。链表是通过指针域的指针链接内存中的各个节点。
所以链表在内存中是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。我们可以根据下图来进行理解。
![image-20201101153659912](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201101153659912.9neaap4ogtc.png)
### 遍历链表
链表的遍历我们通常使用while循环for循环也可以但是代码不够简洁下面我们来看一下链表的遍历代码
for:
```java
for (Node x = first; x != null; x = x.next) {
//处理x.item
}
```
while:
```
Node x = first;
while (x!=null) {
//处理x.item
x = x.next;
}
```
### 链表的几种操作
#### 添加节点
![image-20201101155937520](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201101155937520.my13cevp2cg.png)
#### 删除节点
删除B节点如图所示
![image-20201101155003257](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201101155003257.4onlntrwj2i0.png)
我们只需将A节点的next指针指向C节点即可。
有的同学可能会有这种疑问B节点这样不会留着内存里吗java含有自己的内存回收机制不用自己手动释放内存了但是C++,则需要手动释放。
我们通过上图的删除和插入都是O(1)操作。
链表和数组的比较
| | 插入/删除操作(时间复杂度) | 查询(时间复杂度) | 存储方式 |
| ---- | ------------------------- | ------------------ | ------------ |
| 数组 | O(n) | O(1) | 内存连续分布 |
| 链表 | O(1) | O(n) | 内存散乱分布 |

View File

@ -0,0 +1,45 @@
#### 剑指offer25合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
```
输入1->2->4, 1->3->4
输出1->1->2->3->4->4
```
今天的题目思路很简单但是一遍AC也是不容易的。链表大部分题目考察的都是考生代码的完整性和鲁棒性所以有些题目我们看着思路很简单但是想直接通过还是需要下一翻工夫的所以建议大家将所有链表的题目都自己写一下。实在没有时间做的同学可以自己在脑子里打一遍代码想清没一行代码的作用。
迭代法:
因为我们有两个升序链表我们需要将其合并那么我们需要创建一个新节点headpre然后我们利用双指针思想每个链表放置一个指针然后进行遍历并对比当前指针指向的值。然后headpre.next指向较小值的那个节点不断迭代直至到达某一有序链表底部此时一个链表遍历完成然后我们将未完全遍历的链表接在我们接在合并链表之后即可。
这是我们迭代做法,另外这个题目还有一个递归方法,目前先不写,等链表掌握差不多的时候会单独写一篇关于递归的文章,也算是为树的题目做铺垫。
动图讲解:
![合并数组](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/合并数组.216f4nn4lti8.gif)
```java
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode headpro = new ListNode(-1);
ListNode headtemp = headpro;
while (l1 != null && l2 != null) {
//接上大的那个
if (l1.val >= l2.val) {
headpro.next = l2;
l2 = l2.next;
}
else {
headpro.next = l1;
l1 = l1.next;
}
headpro = headpro.next;
}
headpro.next = l1 != null ? l1:l2;
return headtemp.next;
}
```

View File

@ -0,0 +1,86 @@
#### 剑指 Offer 52. 两个链表的第一个公共节点
### 前言
今天给大家带来一个不是那么难的题目这个题目的解答方法很多只要能AC的就是好方法虽然题目不是特别难但是也是剑指offer上的经典题目所以大家要记得打卡呀。
然后今天我们的链表板块就算结束啦。周末的时候我会对链表的题目做一个总结,俗话说温故而知新嘛。好啦废话不多说,我们一起来看一下今天的题目吧
题目描述:
输入两个链表,找出它们的第一个公共节点。如下图,返回黄色结点即可。
![image-20201029215837844](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/image-20201029215837844.7ezoerpghyk0.png)
题目表达是不是也很简单这个题目我的方法一共有两个一种就是用HashSet进行存储一种就是利用双指针大家有更好的可以在下面讨论呀。
### HashSet
这个方法是比较简单的,主要思路就是,先遍历一个链表将链表的所有值都存到哈希表中,然后再遍历另一个链表,如果发现某个结点在哈希表中已经存在那我们直接返回该节点即可,代码也很简单。
```java
public class Solution {
public ListNode getIntersectionNode (ListNode headA, ListNode headB) {
ListNode tempa = headA;
ListNode tempb = headB;
//定义hash表
HashSet<ListNode> arr = new HashSet<ListNode>();
while (tempa != null) {
arr.add(tempa);
tempa = tempa.next;
}
while (tempb != null) {
if (arr.contains(tempb)) {
return tempb;
}
tempb = tempb.next;
}
return tempb;
}
}
```
下面这个方法比较巧妙,不是特别容易想到,大家可以自己实现一下,这个方法也是利用我们的双指针思想。
下面我们直接看动图吧,特别直观,一下就可以搞懂。
![第一次相交的点](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/第一次相交的点.5nbxf5t3hgk0.gif)
是不是一下就懂了呀,我们利用双指针,当某一指针遍历完链表之后,然后掉头去另一个链表的头部,继续遍历。因为速度相同所以他们第二次遍历的时候肯定会相遇,是不是很浪漫啊!
```java
public class Solution {
public ListNode getIntersectionNode (ListNode headA, ListNode headB) {
//定义两个节点
ListNode tempa = headA;
ListNode tempb = headB;
//循环
while (tempa != tempb) {
//如果不为空就指针下移,为空就跳到另一链表的头部
tempa = tempa!=null ? tempa.next:headB;
tempb = tempb!=null ? tempb.next:headA;
}
return tempa;
}
}
```
好啦,链表的题目就结束啦,希望大家能有所收获,下周就要更新新的题型啦,继续坚持,肯定会有收获的。

View File

@ -0,0 +1,54 @@
# 题目描述链表中倒数第k个节点
题目:
输入一个链表输出该链表中倒数第k个节点。为了符合大多数人的习惯本题从1开始计数即链表的尾节点是倒数第1个节点。例如一个链表有6个节点从头节点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
题目分析:
自己思考一下
我们遇到这个题目,可能会有什么答题思路呢?
你看我说的对不对,是不是会想到先遍历一遍链表知道 链表节点的个数然后再计算出倒数第n个节点。
比如链表长度为10倒数第3个节点不就是正数第8个节点呀这种方法当然可以啦是可以实现的那么我们再思考一下有没有其他方法呢我们可以将链表元素保存到数组里面然后直接就可以知道倒数第K个节点了。这个方法确实比刚才那个方法省时间了但是所耗的空间更多了那我们还有什么方法吗
我们可以继续利用我们的双指针呀,但是我们应该怎么做呢?
双指针法:
首先一个指针移动K-1位这里可以根据你的初始化指针决定然后另一个指针开始启动他俩移动速度一样所以他俩始终相差K-1位当第一个指针到达链表尾部时第二个指针的指向则为倒数第K个节点。
![倒数k个节点](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/倒数k个节点.3uh5e9jvbwc0.gif)
感觉这个方法既巧妙又简单,大家可以自己动手打一下,这个题目是经典题目。
```java
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
//特殊情况
if(head==null){
return head;
}
//初始化两个指针
ListNode pro = new ListNode(-1);
ListNode after = new ListNode(-1);
//定义指针指向
pro = head;
after = head;
//先移动绿指针到指定位置
for(int i = 0; i < k-1; i++) {
pro=pro.next;
}
//两个指针同时移动
while (pro.next != null) {
pro = pro.next;
after = after.next;
}
//返回倒数第k个节点
return after;
}
}
```

View File

@ -0,0 +1,60 @@
#### 面试题 02.03. 删除中间节点
给定一个头结点为 head的非空单链表返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
**示例 1**
```java
输入:[1,2,3,4,5]
输出3
```
> 说明:因为只有一个中间节点
**示例 2**
```java
输入:[1,2,3,4,5,6]
输出4
```
> 说明:有两个中间节点所以返回后面那个
## 题目解析:
又精心筛选了一个题目,本来想写一下删除节点的题目,然后发现这个题目更符合目前的节奏,所以先写一下这个题目,明天再给大家写删除节点的题目。
大家先不要看我的题解先自己想一下怎么做。这个这个题目是想让我们找出中间节点昨天的题目是让我们倒数第K个节点想一下这两个题目有什么联系呢
先说一下刚开始刷题的小伙伴可能会想到的题解,两次遍历链表,第一次遍历获取链表长度,第二次遍历获取中间链表。
这个方法很OK利用数组先将所有链表元素存入数组里然后再直接获得中间节点。这个也很OK那么我们有没有一次遍历且不开辟辅助空间的方法呢
昨天的题目是一前一后双指针两个指针之间始终相差k-1位我们今天也利用一下双指针的做法吧。
这种类型的双指针是我们做链表的题目经常用到的,叫做快慢指针。
一个指针走的快,一个指针走的慢,这个题目我们可以让快指针一次走两步,慢指针一次走一步,当快指针到达链表尾部的时候,慢指针不就到达中间节点了吗?
链表中节点的个数有可能为奇数也有可能为偶数这是两种情况但是我们输出是相同的那就是输出slow指针指向的节点
![中间节点](E:\Typora笔记\CSDN\leetcode通关笔记\博客动图\中间节点.gif)
```java
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;//快指针
ListNode slow = head;//慢指针
//循环条件,思考一下跳出循环的情况
while (fast!=null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//返回slow指针指向的节点
return slow;
}
}
```

View File

@ -0,0 +1,104 @@
#### 面试题 02.05. 链表求和
之前我们一起做了链表中的几个经典题型找到倒数第k个节点找链表中点判断链表中环的起点合并链表反转链表删除链表中重复值。这些是链表中的经典问题面试中也经常会考的问题然后下面我们继续做一道链表题目也是面试中经常会考的题目链表求和问题。
另外有一些小伙伴说,虽然一天一道题不算多,但是每天读题,做题加消化稍微有点跟不上,所以我打算每个周的工作日进行更新题目,到周末的时候对本周的题目进行总结,然后为大家再写一些别的东西。下面我们一起来看一下今天的题目吧。
> 为保证严谨性,所有文章中的代码都在网站进行验证,大家可以放心食用。
题目描述:
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例1
```java
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2)即617 + 295
输出2 -> 1 -> 9即912
```
示例2
```java
输入:(9 -> 9) + (9 -> 9),即99+99
输出8->9->1
```
示例3
```java
输入:(5)+(5),即5+5
输出0->1
```
**题目解析:**
这个题目很容易理解,就是将链表数值进行求和,刚开始做题的同学可能会有这种思路,这个题目我们分别遍历两个链表得到他们的数,然后进行相加,再放到新的链表中,但是这样是行不通的,
因为我们需要考虑溢出的情况java 中 int 型的范围为 -2147483648 到 +2147483648即 -2^31 到 2^31。
所以链表比较长的话进行求和就会溢出,所以我们不能提取过之后再进行相加,
我们应该对链表的每一位进行相加,然后通过链表的和,判断是否需要像下一位进行传递,
就好比小时候我们用竖式进行加法一样判断两位相加是否大于10大于10则进1。
了解了思路,但是想完全实现代码也不是特别容易,这里需要注意的三个点就是,
1.我们需要根据两个链表的长度,不断对新链表添加节点
2.需要创建一个变量用来保存进位值。
3.当跳出循环之后需要根据进位值来判断需不需要再对链表长度加1.
这三条可以结合代码理解进行。
进位值只能是0或1因为每一位最大为99+9=18
![链表求和](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/链表求和.1yh4ymdee3k0.gif)
这里需要注意得时链表遍历结束我们应该跳出循环但是我们的nlist仍在尾部添加了1节点那是因为跳出循环时summod值为1所以我们需要在尾部再添加一个节点。
```java
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//返回链表
ListNode nList = new ListNode(-1);
ListNode tempnode = nList;
//用来保存进位值初始化为0
int summod = 0;
while(l1 != null || l2 != null) {
//如果l1的链表为空则l1num为0若是不为空则为链表的节点值
//判断是否为空为空就设为0
int l1num = l1 == null ? 0 : l1.val;
int l2num = l2 == null ? 0 : l2.val;
//将链表的值和进位值相加,得到为返回链表的值
int sum = l1num+l2num+summod;
//更新进位值例18/10=19/10=0
summod = sum/10;
//新节点保存的值18%8=8则添加8
sum = sum%10;
//添加节点
tempnode.next = new ListNode(sum);
//移动指针
tempnode = tempnode.next;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
//最后根据进位值判断需不需要继续添加节点
if (summod == 1) {
tempnode.next = new ListNode(summod);
}
return nList.next;
}
}
```