LinkedList
Kang Lv3

链表

基础知识

单链表

  • 结构
1
2
3
4
5
6
7
8
 class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
  • 添加操作
1
2
3
4
5
6
cur.next = prev.next;
prev.next = cur;

// 添加头结点
cur.next = this.head;
this.head = cur;
  • 删除操作
1
prev.next = cur.next;

707

双指针

  • 快慢指针或先后指针

模板

  • 在调用 next 字段之前,始终检查节点是否为空
  • 仔细定义循环的结束条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Initialize slow & fast pointers
ListNode slow=head;
ListNode fast=head;
/*
Change this condition to fit specific problem.
Attention: remember to avoid null-pointer error
*/
while(slow!=null&&fast!=null&&fast.next!=null){
slow=slow.next; // move slow pointer one step each time
fast=fast.next.next; // move fast pointer two steps each time
if(slow==fast){ // change this condition to fit specific problem
return true;
}
}
return false; // change return value to fit specific problem

141 142 160 19 203

经典问题

反转链表
  • 按原始顺序迭代结点,并将它们逐个移动到列表的头部

206

其他
  • 移除链表元素
  • 奇偶链表

203 328

回文链表
  • 因为回文串是对称的,所以正着读和倒着读应该是一样的,这一特点是解决回文串问题的关键。
1
2
3
4
5
6
7
// 倒序打印单链表中的元素值
void traverse(ListNode head) {
if (head == null) return;
traverse(head.next);
// 后序遍历代码
print(head.val);
}

324

双链表

  • 节点结构
1
2
3
4
5
class DoublyListNode {
int val;
DoublyListNode next, prev;
DoublyListNode(int x) {val = x};
}
  • 添加节点
1
2
3
4
5
6
7
8
// 前一节点prev
// 后一节点next
// 需插入节点cur

cur.next = prev.next;
cur.prev = prev;
prev.next.prev = cur;
pre.next = cur
  • 删除节点
1
2
3
// 删除当前节点cur
cur.prev.next = cur.next;
cur.next.prev = cur.prev;

小结

21 2 430

相关题目

707. 设计链表

设计链表的实现。 您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val和next。val是当前节点的值,next是指向下一个节点的指针引用。如果要使用双向链表,则还需要一个属性prev以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第index个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为val的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第index个节点之前添加值为val 的节点。如果index等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引index 有效,则删除链表中的第index 个节点。
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class MyLinkedList {
class Node {
int val;
Node next;

Node(int val) {
this.val = val;
}
}

int size;
Node head;

public MyLinkedList() {
this.size = 0;
this.head = null;
}

public int get(int index) {
if (index >= size || index < 0 || head == null) return -1;
Node temp = this.head;
for (int i = 0; i < index; i++) temp = temp.next;
return temp.val;
}

public void addAtHead(int val) {
Node node = new Node(val);
node.next = this.head;
this.head = node;
size++;

}

public void addAtTail(int val) {
Node node = new Node(val);
Node temp = this.head;
if (size == 0) {
this.head = node;
node.next = null;

} else {
while (temp.next != null) {
temp = temp.next;
}
node.next = null;
temp.next = node;
}
size++;
}

public void addAtIndex(int index, int val) {
if (index > size || index < 0) return;
if (index == 0) {
addAtHead(val);
return;
}
if (index == this.size) {
addAtTail(val);
return;
}
Node prev = this.head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
Node cur = new Node(val);
cur.next = prev.next;
prev.next = cur;
size++;
}

public void deleteAtIndex(int index) {
if (index >= size || index < 0) return;
Node temp = this.head;
if (index == 0) {
this.head = this.head.next;
} else {
for (int i = 0; i < index - 1; i++) {
temp = temp.next;
}
temp.next = temp.next.next;
}
size--;
}
}

/*
Your MyLinkedList object will be instantiated and called as such:
MyLinkedList obj = new MyLinkedList();
int param_1 = obj.get(index);
obj.addAtHead(val);
obj.addAtTail(val);
obj.addAtIndex(index,val);
obj.deleteAtIndex(index);
*/

141. 环形链表

给定一个链表,判断链表中是否有环。

  • 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
  • 如果链表中存在环,则返回 true 。 否则,返回 false 。
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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
boolean hasCycle(ListNode head) {
// 快慢指针初始化指向 head
ListNode slow = head, fast = head;
// 快指针走到末尾时停止
while (fast != null && fast.next != null) {
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
// 快慢指针相遇,说明含有环
if (slow == fast) {
return true;
}
}
// 不包含环
return false;
}
}
}

142. 环形链表 II

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

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

img.png

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
/**
* 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, slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
// 上面的代码类似 hasCycle 函数
if (fast == null || fast.next == null) {
// fast 遇到空指针说明没有环
return null;
}

// 重新指向头结点
slow = head;
// 快慢指针同步前进,相交点就是环起点
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}

160. 相交链表

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode top = headA, bot = headB;
while (top != bot) {
top = (top == null ? headB : top.next);
bot = (bot == null ? headA : bot.next);
}
return top;
}
}

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

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

  • 对头节点单独考虑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 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 slow = head, fast = head;
for (int i=0; i<n; i++) fast = fast.next;
// 特殊情况,删除的是头结点
if (fast == null) return head.next;
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return head;
}
}
  • 要删除倒数第 n 个节点,就得获得倒数第 n + 1 个节点的引用
  • 但有了我们虚拟节点 pre 的存在,就避免了无法获取头结点的前一个节点的问题,能够对这种情况进行正确的删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 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 pre = new ListNode(-1, head);
ListNode slow = pre, fast = pre;
for (int i=0; i<n; i++) fast = fast.next;
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return pre.next;
}
}

206. 反转链表

给你单链表的头节点 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
/**
* 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) {
if (head == null) return null;
ListNode begin = head, end = head;
ListNode temp = head.next;
while (end.next != null) {
end.next = temp.next;
temp.next = begin;
begin = temp;
temp = end.next;
}
return begin;
}
}
  • 参考,迭代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 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 prev = null, curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 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 removeElements(ListNode head, int val) {
ListNode headPre = new ListNode(-1, head);
ListNode temp = headPre;
while (temp.next != null) {
if (temp.next.val == val) temp.next = temp.next.next;
else temp = temp.next;
}
return headPre.next;
}
}

328. 奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

  • 注意判断条件,secondTemp != null && secondTemp.next != null
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 oddEvenList(ListNode head) {
if (head == null) return null;
ListNode secondHead = head.next;
ListNode firstTemp = head, secondTemp = secondHead;
while (secondTemp != null && secondTemp.next != null) {
firstTemp.next = secondTemp.next;
firstTemp = firstTemp.next;
secondTemp.next = firstTemp.next;
secondTemp = secondTemp.next;
}
firstTemp.next = secondHead;
return head;
}
}

234. 回文链表

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

后序遍历
  • 将整个链表后序遍历一遍
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
/**
* 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 {
// 左侧指针
ListNode left;
public boolean isPalindrome(ListNode head) {
left = head;
return traverse(head);
}

boolean traverse(ListNode right) {
if (right == null) return true;
boolean res = traverse(right.next);
// 后序遍历代码
res = res && (right.val == left.val);
left = left.next;
return res;
}
}

复杂度都为O(n)

优化空间复杂度

时间复杂度为O(n),空间复杂度为O(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
boolean isPalindrome(ListNode head) {
ListNode slow, fast;
slow = fast = head;
// 利用快慢指针找中点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 如果长度为奇数,则slow指针多移动一个位置
if (fast != null)
slow = slow.next;

// 反转后面一半的链表
ListNode left = head;
ListNode right = reverse(slow);
while (right != null) {
if (left.val != right.val)
return false;
left = left.next;
right = right.next;
}

return true;
}

ListNode reverse(ListNode head) {
ListNode pre = null, cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。
新链表是通过拼接给定的两个链表的所有节点组成的。

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
/**
* 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 mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;

ListNode head = new ListNode();
ListNode res = head;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
head.next = l1;
l1 = l1.next;
} else {
head.next = l2;
l2 = l2.next;
}
head = head.next;
}
if (l1 == null) head.next = l2;
if (l2 == null) head.next = l1;
return res.next;
}
}

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

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
/**
* 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 addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null, tail = null;
int carry = 0;
while (l1 != null || l2 != null) {
int n1 = l1 == null ? 0 : l1.val;
int n2 = l2 == null ? 0 : l2.val;
int sum = n1 + n2 + carry;
if (head == null) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;

carry = sum / 10;
}
if (carry > 0) tail.next = new ListNode(carry);
return head;
}
}

430. 扁平化多级双向链表

多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构。
给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。

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
/*
// Definition for a Node.
class Node {
public int val;
public Node prev;
public Node next;
public Node child;
};
*/
class Solution {
public Node flatten(Node head) {
dfs(head);
return head;
}

public Node dfs(Node node) {
Node cur = node;
// 记录链表的最后一个节点
Node last = null;

while (cur != null) {
Node next = cur.next;
// 如果有子节点,那么首先处理子节点
if (cur.child != null) {
Node childLast = dfs(cur.child);

next = cur.next;
// 将 node 与 child 相连
cur.next = cur.child;
cur.child.prev = cur;

// 如果 next 不为空,就将 last 与 next 相连
if (next != null) {
childLast.next = next;
next.prev = childLast;
}

// 将 child 置为空
cur.child = null;
last = childLast;
} else {
last = cur;
}
cur = next;
}
return last;
}
}
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
/*
// Definition for a Node.
class Node {
public int val;
public Node prev;
public Node next;
public Node child;
};
*/

class Solution {
public Node flatten(Node head) {
dfs(head);
return head;
}

public Node dfs(Node head) {
Node last = head;
while (head != null) {
if (head.child == null) {
last = head;
head = head.next;
} else {
Node temp = head.next;
Node childLast = dfs(head.child);
head.next = head.child;
head.next.prev = head;
head.child = null;
if (childLast != null) childLast.next = temp;
if (temp != null) temp.prev = childLast;
last = head;
head = childLast;
}
}
return last;
}
}
  • 本文标题:LinkedList
  • 本文作者:Kang
  • 创建时间:2021-11-13 17:26:27
  • 本文链接:ykhou.github.io2021/11/13/LinkedList/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!