Skip to content

Commit cec7832

Browse files
committed
增加链表相关算法题
1 parent c1ce439 commit cec7832

File tree

1 file changed

+392
-0
lines changed

1 file changed

+392
-0
lines changed
Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
2+
# 1. 两数相加
3+
4+
### 题目描述
5+
6+
> Leetcode:给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
7+
>
8+
>你可以假设除了数字 0 之外,这两个数字都不会以零开头。
9+
10+
示例:
11+
12+
```
13+
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
14+
输出:7 -> 0 -> 8
15+
原因:342 + 465 = 807
16+
```
17+
18+
### 问题分析
19+
20+
Leetcode官方详细解答地址:
21+
22+
https://leetcode-cn.com/problems/add-two-numbers/solution/
23+
24+
> 要对头结点进行操作时,考虑创建哑节点dummy,使用dummy->next表示真正的头节点。这样可以避免处理头节点为空的边界问题。
25+
26+
我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐
27+
位相加的过程。
28+
29+
![图1,对两数相加方法的可视化: 342 + 465 = 807342+465=807, 每个结点都包含一个数字,并且数字按位逆序存储。](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/34910956.jpg)
30+
31+
### Solution
32+
33+
**我们首先从最低有效位也就是列表 l1和 l2 的表头开始相加。注意需要考虑到进位的情况!**
34+
35+
```java
36+
/**
37+
* Definition for singly-linked list.
38+
* public class ListNode {
39+
* int val;
40+
* ListNode next;
41+
* ListNode(int x) { val = x; }
42+
* }
43+
*/
44+
//https://leetcode-cn.com/problems/add-two-numbers/description/
45+
class Solution {
46+
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
47+
ListNode dummyHead = new ListNode(0);
48+
ListNode p = l1, q = l2, curr = dummyHead;
49+
//carry 表示进位数
50+
int carry = 0;
51+
while (p != null || q != null) {
52+
int x = (p != null) ? p.val : 0;
53+
int y = (q != null) ? q.val : 0;
54+
int sum = carry + x + y;
55+
//进位数
56+
carry = sum / 10;
57+
//新节点的数值为sum % 10
58+
curr.next = new ListNode(sum % 10);
59+
curr = curr.next;
60+
if (p != null) p = p.next;
61+
if (q != null) q = q.next;
62+
}
63+
if (carry > 0) {
64+
curr.next = new ListNode(carry);
65+
}
66+
return dummyHead.next;
67+
}
68+
}
69+
```
70+
71+
# 2. 翻转链表
72+
73+
74+
### 题目描述
75+
> 剑指 offer:输入一个链表,反转链表后,输出链表的所有元素。
76+
77+
![翻转链表](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/81431871.jpg)
78+
79+
### 问题分析
80+
81+
这道算法题,说直白点就是:如何让后一个节点指向前一个节点!在下面的代码中定义了一个 next 节点,该节点主要是保存要反转到头的那个节点,防止链表 “断裂”。
82+
83+
### Solution
84+
85+
86+
```java
87+
public class ListNode {
88+
int val;
89+
ListNode next = null;
90+
91+
ListNode(int val) {
92+
this.val = val;
93+
}
94+
}
95+
```
96+
97+
```java
98+
/**
99+
*
100+
* @author Snailclimb
101+
* @date 2018年9月19日
102+
* @Description: TODO
103+
*/
104+
public class Solution {
105+
106+
public ListNode ReverseList(ListNode head) {
107+
108+
ListNode next = null;
109+
ListNode pre = null;
110+
111+
while (head != null) {
112+
// 保存要反转到头的那个节点
113+
next = head.next;
114+
// 要反转的那个节点指向已经反转的上一个节点(备注:第一次反转的时候会指向null)
115+
head.next = pre;
116+
// 上一个已经反转到头部的节点
117+
pre = head;
118+
// 一直向链表尾走
119+
head = next;
120+
}
121+
return pre;
122+
}
123+
124+
}
125+
```
126+
127+
测试方法:
128+
129+
```java
130+
public static void main(String[] args) {
131+
132+
ListNode a = new ListNode(1);
133+
ListNode b = new ListNode(2);
134+
ListNode c = new ListNode(3);
135+
ListNode d = new ListNode(4);
136+
ListNode e = new ListNode(5);
137+
a.next = b;
138+
b.next = c;
139+
c.next = d;
140+
d.next = e;
141+
new Solution().ReverseList(a);
142+
while (e != null) {
143+
System.out.println(e.val);
144+
e = e.next;
145+
}
146+
}
147+
```
148+
149+
输出:
150+
151+
```
152+
5
153+
4
154+
3
155+
2
156+
1
157+
```
158+
159+
# 3. 链表中倒数第k个节点
160+
161+
### 题目描述
162+
163+
> 剑指offer: 输入一个链表,输出该链表中倒数第k个结点。
164+
165+
### 问题分析
166+
167+
首先两个节点/指针,一个节点 node1 先开始跑,指针 node1 跑到 k-1 个节点后,另一个节点 node2 开始跑,当 node1 跑到最后时,node2 所指的节点就是倒数第k个节点。
168+
169+
170+
### Solution
171+
172+
```java
173+
/*
174+
public class ListNode {
175+
int val;
176+
ListNode next = null;
177+
178+
ListNode(int val) {
179+
this.val = val;
180+
}
181+
}*/
182+
183+
//时间复杂度O(n),一次遍历即可
184+
//https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
185+
public class Solution {
186+
public ListNode FindKthToTail(ListNode head,int k) {
187+
//如果链表为空或者k小于等于0
188+
if(head==null||k<=0){
189+
return null;
190+
}
191+
// 声明两个指向头结点的节点
192+
ListNode node1 = head, node2 = head;
193+
//记录节点的个数
194+
int count=0;
195+
//记录k值,后面要使用
196+
int index=k;
197+
//p指针先跑,并且记录节点数,当node1节点跑了k-1个节点后,node2节点开始跑,
198+
//当node1节点跑到最后时,node2节点所指的节点就是倒数第k个节点
199+
while(node1!=null){
200+
node1=node1.next;
201+
count++;
202+
if(k<1){
203+
node2=node2.next;
204+
}
205+
k--;
206+
}
207+
//如果节点个数小于所求的倒数第k个节点,则返回空
208+
if(count<index) return null;
209+
return node2;
210+
211+
}
212+
}
213+
214+
```
215+
216+
217+
# 4. 删除链表的倒数第N个节点
218+
219+
> Leetcode:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
220+
221+
**示例:**
222+
223+
```
224+
给定一个链表: 1->2->3->4->5, 和 n = 2.
225+
226+
当删除了倒数第二个节点后,链表变为 1->2->3->5.
227+
228+
```
229+
230+
**说明:**
231+
232+
给定的 n 保证是有效的。
233+
234+
**进阶:**
235+
236+
你能尝试使用一趟扫描实现吗?
237+
238+
该题在 leetcode 上有详细解答,具体可参考 Leetcode.
239+
240+
### 问题分析
241+
242+
我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 LL 是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。
243+
244+
![图 1. 删除列表中的第 L - n + 1 个元素](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/94354387.jpg)
245+
246+
### Solution
247+
248+
**两次遍历法**
249+
250+
首先我们将添加一个 **哑结点** 作为辅助,该结点位于列表头部。哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部。在第一次遍历中,我们找出列表的长度 L。然后设置一个指向哑结点的指针,并移动它遍历列表,直至它到达第 (L - n) 个结点那里。我们把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点,完成这个算法。
251+
252+
```java
253+
/**
254+
* Definition for singly-linked list.
255+
* public class ListNode {
256+
* int val;
257+
* ListNode next;
258+
* ListNode(int x) { val = x; }
259+
* }
260+
*/
261+
// https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/description/
262+
public class Solution {
263+
public ListNode removeNthFromEnd(ListNode head, int n) {
264+
// 哑结点,哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部
265+
ListNode dummy = new ListNode(0);
266+
// 哑结点指向头结点
267+
dummy.next = head;
268+
// 保存链表长度
269+
int length = 0;
270+
ListNode len = head;
271+
while (len != null) {
272+
length++;
273+
len = len.next;
274+
}
275+
length = length - n;
276+
ListNode target = dummy;
277+
// 找到 L-n 位置的节点
278+
while (length > 0) {
279+
target = target.next;
280+
length--;
281+
}
282+
// 把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点
283+
target.next = target.next.next;
284+
return dummy.next;
285+
}
286+
}
287+
```
288+
289+
复杂度分析:
290+
291+
- 时间复杂度 O(L) :该算法对列表进行了两次遍历,首先计算了列表的长度 LL 其次找到第 (L - n)(L−n) 个结点。 操作执行了 2L-n2L−n 步,时间复杂度为 O(L)O(L)。
292+
293+
- 空间复杂度 O(1) :我们只用了常量级的额外空间。
294+
295+
296+
297+
**进阶——一次遍历法:**
298+
299+
其实这种方法就和我们上面第四题找“链表中倒数第k个节点”所用的思想是一样的。**基本思路就是:** 定义两个节点 node1、node2;node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点)
300+
301+
```java
302+
/**
303+
* Definition for singly-linked list.
304+
* public class ListNode {
305+
* int val;
306+
* ListNode next;
307+
* ListNode(int x) { val = x; }
308+
* }
309+
*/
310+
public class Solution {
311+
public ListNode removeNthFromEnd(ListNode head, int n) {
312+
313+
ListNode dummy = new ListNode(0);
314+
dummy.next = head;
315+
// 声明两个指向头结点的节点
316+
ListNode node1 = dummy, node2 = dummy;
317+
318+
// node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑
319+
//当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点)
320+
while (node1 != null) {
321+
node1 = node1.next;
322+
if (n < 0) {
323+
node2 = node2.next;
324+
}
325+
n--;
326+
}
327+
328+
node2.next = node2.next.next;
329+
330+
return dummy.next;
331+
332+
}
333+
}
334+
```
335+
336+
337+
338+
339+
340+
# 5. 合并两个排序的链表
341+
342+
### 题目描述
343+
344+
> 剑指offer:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
345+
346+
### 问题分析
347+
348+
我们可以这样分析:
349+
350+
1. 假设我们有两个链表 A,B;
351+
2. A的头节点A1的值与B的头结点B1的值比较,假设A1小,则A1为头节点;
352+
3. A2再和B1比较,假设B1小,则,A1指向B1;
353+
4. A2再和B2比较
354+
就这样循环往复就行了,应该还算好理解。
355+
356+
考虑通过递归的方式实现!
357+
358+
### Solution
359+
360+
**递归版本:**
361+
362+
```java
363+
/*
364+
public class ListNode {
365+
int val;
366+
ListNode next = null;
367+
368+
ListNode(int val) {
369+
this.val = val;
370+
}
371+
}*/
372+
//https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
373+
public class Solution {
374+
public ListNode Merge(ListNode list1,ListNode list2) {
375+
if(list1 == null){
376+
return list2;
377+
}
378+
if(list2 == null){
379+
return list1;
380+
}
381+
if(list1.val <= list2.val){
382+
list1.next = Merge(list1.next, list2);
383+
return list1;
384+
}else{
385+
list2.next = Merge(list1, list2.next);
386+
return list2;
387+
}
388+
}
389+
}
390+
```
391+
392+

0 commit comments

Comments
 (0)