链表


2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1

输入:l1 = [2,4,3], l2 = [5,6,4]

输出:[7,0,8]

解释:342 + 465 = 807.

示例 2

输入:l1 = [0], l2 = [0]

输出:[0]

示例 3

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]

输出:[8,9,9,9,0,0,0,1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode();
ListNode cur = dummy;

int flag = 0;
while(l1!=null||l2!=null) {
int x = l1 != null ? l1.val : 0;
int y = l2 != null ? l2.val : 0;
int sum = x+y+flag;

flag = sum/10;
sum = sum%10;

cur.next = new ListNode(sum);
cur = cur.next;
if(l1!=null) {
l1 = l1.next;
}
if(l2!=null) {
l2 = l2.next;
}
}

if(flag!=0) {
cur.next = new ListNode(flag);
}

return dummy.next;
}
}

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1


输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

示例 2

输入:head = [1,2]

输出:[2,1]

示例 3

输入:head = []

输出:[]

思路草稿

反转链表

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;

while(cur!=null) {
ListNode temp = cur.next;
cur.next = pre;
pre=cur;
cur = temp;
}

return pre;
}
}

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

示例 1

输入:head = [1,2,2,1]

输出:true

示例 2

输入:head = [1,2]

输出:false

题解

转换链表判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public boolean isPalindrome(ListNode head) {
List<Integer> list = new ArrayList<>();
ListNode cur = head;
while(cur!=null) {
list.add(cur.val);
cur = cur.next;
}

int left = 0;
int right = list.size() - 1;

while(left<right) {
if(list.get(left)!=list.get(right)) {
return false;
}
left++;
right--;
}

return true;
}
}

快慢指针,分割链表,反转后比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode slow = head;
ListNode fast = head;

while(fast.next!=null&&fast.next.next!=null) {
slow = slow.next;
fast = fast.next.next;
}

ListNode half = reverseList(slow.next);

ListNode n1 = head;
ListNode n2 = half;

while(n1!=null&&n2!=null) {
if(n1.val!=n2.val) {
slow.next = reverseList(half);
return false;
}
n1 = n1.next;
n2 = n2.next;
}

slow.next = reverseList(half);

return true;
}

private ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;

while(cur!=null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}

return pre;
}
}

24. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1

输入:head = [1,2,3,4]

输出:[2,1,4,3]

思路草稿

两两交换链表中的节点

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode();
dummy.next = head;
ListNode cur = dummy;

while(cur.next!=null&&cur.next.next!=null){
ListNode node1 = cur.next;
ListNode node2 = cur.next.next;
cur.next = node2;
node1.next = node2.next;
node2.next = node1;
cur = node1;
}

return dummy.next;
}
}

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1

输入:head = [1,2,3,4,5], n = 2

输出:[1,2,3,5]

思路草稿

删除链表的倒数第 N 个结点

只需将 fast 指针先移动 n 次,让 fast 与 slow 相差 n 个节点即可,这样就能保证 fast 指针走到尾节点时,slow 位于待删除元素的前一个位置。

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0,head);
ListNode fast = dummy;
ListNode slow = dummy;

for(int i = 0;i<n;i++){
fast = fast.next;
}

while(fast.next!=null){
slow = slow.next;
fast = fast.next;
}

slow.next = slow.next.next;

return dummy.next;
}
}

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

链表相交题意

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3

输出:Intersected at '8'

解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。

从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。

在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while (curA != null) { // 求链表A的长度
lenA++;
curA = curA.next;
}
while (curB != null) { // 求链表B的长度
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}

}

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1

输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点

解释:链表中有一个环,其尾部连接到第二个节点。

思路草稿

环形链表II

环形链表II-1

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;

do {
if(fast==null||fast.next==null){
return null;
}

slow = slow.next;
fast = fast.next.next;
} while(slow!=fast);

ListNode index1 = head;
ListNode index2 = slow;

while(index1!=index2){
index1 = index1.next;
index2 = index2.next;
}

return index1;
}
}

25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1

输入:head = [1,2,3,4,5], k = 2

输出:[2,1,4,3,5]

示例 2

输入:head = [1,2,3,4,5], k = 3

输出:[3,2,1,4,5]

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0,head);

ListNode pre = dummy;
ListNode end = dummy;

while(end.next!=null){
// 先将 end 移动到 k 的位置
for(int i = 0;i<k&&end!=null;i++){
end = end.next;
}
if(end==null){
break;
}
// start: k 个节点的头节点
// end: k 个节点的尾节点
ListNode start = pre.next;
// next: 保存下一组节点的头节点
ListNode next = end.next;
end.next = null; // 切断 k 个节点 进行分组
pre.next = reverseList(start);
start.next = next; // start 此时已经为 尾节点,下一个节点为 下一组节点的头节点
pre = start; // 刷新 pre 前置位
end = pre; // 刷新 end 起始位
}
return dummy.next;
}

private ListNode reverseList(ListNode node){
ListNode pre = null;
ListNode cur = node;

while(cur!=null){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}

return pre;
}
}