LC 707. 设计链表

题目描述

这是 LeetCode 上的 707. 设计链表 ,难度为 中等

设计链表的实现。您可以选择使用单链表或双链表。

单链表中的节点应该具有两个属性:valnextval 是当前节点的值,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
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3

提示:

  • 所有 val 值都在 $[1, 1000]$ 之内。
  • 操作次数将在 $[1, 1000]$ 之内。
  • 请不要使用内置的 LinkedList 库。

双向链表

一道手写链表裸题(啊嘿,$9$ 月活动的同学表示很棒 🤣

由于需要涉及部分 index 相关操作,因此我们可以实现一个「双向链表」,同时使用变量 sz 记录下当前链表的总长度,这样在涉及 index 操作时,可从较近的一边出发遍历,剩下的则是常规的链表节点操作。

一些细节:所有的链表题目我们都可以引入前后哨兵来简化边界处理;同时能写双链表自然能写单链表,因此如果你没有顺利写出双链表的话,在看了题解写完双链表后,再手写一遍单链表作为练习。

Java 代码:

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
class MyLinkedList {
class Node {
Node prev, next;
int val;
Node (int _val) {
val = _val;
}
}
Node he = new Node(-1), ta = new Node(-1);
int sz = 0;
public MyLinkedList() {
he.next = ta; ta.prev = he;
}
public int get(int index) {
Node node = getNode(index);
return node == null ? -1 : node.val;
}
public void addAtHead(int val) {
Node node = new Node(val);
node.next = he.next; node.prev = he;
he.next.prev = node; he.next = node;
sz++;
}
public void addAtTail(int val) {
Node node = new Node(val);
node.prev = ta.prev; node.next = ta;
ta.prev.next = node; ta.prev = node;
sz++;
}
public void addAtIndex(int index, int val) {
if (index > sz) return ;
if (index <= 0) {
addAtHead(val);
} else if (index == sz) {
addAtTail(val);
} else {
Node node = new Node(val), cur = getNode(index);
node.next = cur; node.prev = cur.prev;
cur.prev.next = node; cur.prev = node;
sz++;
}
}
public void deleteAtIndex(int index) {
Node cur = getNode(index);
if (cur == null) return ;
cur.next.prev = cur.prev;
cur.prev.next = cur.next;
sz--;
}
Node getNode(int index) {
boolean isLeft = index < sz / 2;
if (!isLeft) index = sz - index - 1;
for (Node cur = isLeft ? he.next : ta.prev; cur != ta && cur != he; cur = isLeft ? cur.next : cur.prev) {
if (index-- == 0) return cur;
}
return null;
}
}

TypeScript 代码:
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
class TNode {
prev: TNode
next: TNode
val: number
constructor(_val: number) {
this.val = _val
}
}
class MyLinkedList {
he = new TNode(-1)
ta = new TNode(-1)
sz = 0
constructor() {
this.he.next = this.ta; this.ta.prev = this.he
}
get(index: number): number {
const node = this.getNode(index)
return node == null ? -1 : node.val
}
addAtHead(val: number): void {
const node = new TNode(val)
node.next = this.he.next; node.prev = this.he
this.he.next.prev = node; this.he.next = node
this.sz++
}
addAtTail(val: number): void {
const node = new TNode(val)
node.prev = this.ta.prev; node.next = this.ta
this.ta.prev.next = node; this.ta.prev = node
this.sz++
}
addAtIndex(index: number, val: number): void {
if (index > this.sz) return
if (index <= 0) {
this.addAtHead(val)
} else if (index == this.sz) {
this.addAtTail(val)
} else {
const node = new TNode(val), cur = this.getNode(index)
node.next = cur; node.prev = cur.prev
cur.prev.next = node; cur.prev = node
this.sz++
}
}
deleteAtIndex(index: number): void {
const cur = this.getNode(index)
if (cur == null) return
cur.prev.next = cur.next
cur.next.prev = cur.prev
this.sz--
}
getNode(index: number): TNode | null {
const isLeft = index < this.sz / 2
if (!isLeft) index = this.sz - index - 1
for (let cur = isLeft ? this.he.next : this.ta.prev; cur != this.ta && cur != this.he; cur = isLeft ? cur.next : cur.prev) {
if (index-- == 0) return cur
}
return null
}
}

Python 代码:
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
class Node:
def __init__(self, _val):
self.val = _val
self.prev = None
self.next = None

class MyLinkedList:
def __init__(self):
self.he = Node(-1)
self.ta = Node(-1)
self.he.next = self.ta
self.ta.prev = self.he
self.sz = 0

def get(self, index: int) -> int:
node = self.getNode(index)
return node.val if node else -1

def addAtHead(self, val: int) -> None:
node = Node(val)
node.next = self.he.next
node.prev = self.he
self.he.next.prev = node
self.he.next = node
self.sz += 1

def addAtTail(self, val: int) -> None:
node = Node(val)
node.prev = self.ta.prev
node.next = self.ta
self.ta.prev.next = node
self.ta.prev = node
self.sz += 1

def addAtIndex(self, index: int, val: int) -> None:
if index > self.sz:
return
if index <= 0:
self.addAtHead(val)
elif index == self.sz:
self.addAtTail(val)
else:
node, cur = Node(val), self.getNode(index)
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
self.sz += 1

def deleteAtIndex(self, index: int) -> None:
node = self.getNode(index)
if node:
node.prev.next = node.next
node.next.prev = node.prev
self.sz -= 1

def getNode(self, index: int) -> Node | None:
isLeft = index < self.sz / 2
if not isLeft:
index = self.sz - index - 1
cur = self.he.next if isLeft else self.ta.prev
while cur != self.he and cur != self.ta:
if index == 0:
return cur
index -= 1
cur = cur.next if isLeft else cur.prev
return None

  • 时间复杂度:涉及 index 的相关操作复杂度为 $O(index)$;其余操作均为 $O(1)$
  • 空间复杂度:$O(n)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.707 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!