--- comments: true difficulty: 中等 edit_url: https://github.com/doocs/leetcode/edit/main/solution/0700-0799/0707.Design%20Linked%20List/README.md tags: - 设计 - 链表 --- # [707. 设计链表](https://leetcode.cn/problems/design-linked-list) [English Version](/solution/0700-0799/0707.Design%20Linked%20List/README_EN.md) ## 题目描述

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

 

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

 

提示:

## 解法 ### 方法一:指针引用实现单链表 我们创建链表虚拟头节点 `dummy`,用变量 `cnt` 记录当前链表节点个数。 具体的方法如下: - `get(index)`:遍历链表,找到第 `index` 个节点,返回其值,如果不存在,返回 $-1$。时间复杂度 $O(n)$。 - `addAtHead(val)`:创建新节点,将其插入到虚拟头节点后面。时间复杂度 $O(1)$。 - `addAtTail(val)`:创建新节点,将其插入到链表尾部。时间复杂度 $O(n)$。 - `addAtIndex(index, val)`:如果 `index` 等于链表长度,则该节点将附加到链表的末尾。如果 `index` 大于链表长度,则不会插入节点。如果 `index` 小于 $0$,则在头部插入节点。否则,遍历链表,找到第 `index` 个节点的前一个节点,将新节点插入到该节点后面。时间复杂度 $O(n)$。 - `deleteAtIndex(index)`:如果索引 `index` 有效,则删除链表中的第 `index` 个节点。否则,不做任何操作。时间复杂度 $O(n)$。 时间复杂度见具体的方法说明。其中 $n$ 为链表长度。 注意:LeetCode 平台已经内置 ListNode 单链表节点类,可以直接使用。 #### Python3 ```python class MyLinkedList: def __init__(self): self.dummy = ListNode() self.cnt = 0 def get(self, index: int) -> int: if index < 0 or index >= self.cnt: return -1 cur = self.dummy.next for _ in range(index): cur = cur.next return cur.val def addAtHead(self, val: int) -> None: self.addAtIndex(0, val) def addAtTail(self, val: int) -> None: self.addAtIndex(self.cnt, val) def addAtIndex(self, index: int, val: int) -> None: if index > self.cnt: return pre = self.dummy for _ in range(index): pre = pre.next pre.next = ListNode(val, pre.next) self.cnt += 1 def deleteAtIndex(self, index: int) -> None: if index >= self.cnt: return pre = self.dummy for _ in range(index): pre = pre.next t = pre.next pre.next = t.next t.next = None self.cnt -= 1 # Your MyLinkedList object will be instantiated and called as such: # obj = MyLinkedList() # param_1 = obj.get(index) # obj.addAtHead(val) # obj.addAtTail(val) # obj.addAtIndex(index,val) # obj.deleteAtIndex(index) ``` #### Java ```java class MyLinkedList { private ListNode dummy = new ListNode(); private int cnt; public MyLinkedList() { } public int get(int index) { if (index < 0 || index >= cnt) { return -1; } var cur = dummy.next; while (index-- > 0) { cur = cur.next; } return cur.val; } public void addAtHead(int val) { addAtIndex(0, val); } public void addAtTail(int val) { addAtIndex(cnt, val); } public void addAtIndex(int index, int val) { if (index > cnt) { return; } var pre = dummy; while (index-- > 0) { pre = pre.next; } pre.next = new ListNode(val, pre.next); ++cnt; } public void deleteAtIndex(int index) { if (index < 0 || index >= cnt) { return; } var pre = dummy; while (index-- > 0) { pre = pre.next; } var t = pre.next; pre.next = t.next; t.next = null; --cnt; } } /** * 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); */ ``` #### C++ ```cpp class MyLinkedList { private: ListNode* dummy = new ListNode(); int cnt = 0; public: MyLinkedList() { } int get(int index) { if (index < 0 || index >= cnt) { return -1; } auto cur = dummy->next; while (index--) { cur = cur->next; } return cur->val; } void addAtHead(int val) { addAtIndex(0, val); } void addAtTail(int val) { addAtIndex(cnt, val); } void addAtIndex(int index, int val) { if (index > cnt) { return; } auto pre = dummy; while (index-- > 0) { pre = pre->next; } pre->next = new ListNode(val, pre->next); ++cnt; } void deleteAtIndex(int index) { if (index >= cnt) { return; } auto pre = dummy; while (index-- > 0) { pre = pre->next; } auto t = pre->next; pre->next = t->next; t->next = nullptr; --cnt; } }; /** * 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); */ ``` #### Go ```go type MyLinkedList struct { dummy *ListNode cnt int } func Constructor() MyLinkedList { return MyLinkedList{&ListNode{}, 0} } func (this *MyLinkedList) Get(index int) int { if index < 0 || index >= this.cnt { return -1 } cur := this.dummy.Next for ; index > 0; index-- { cur = cur.Next } return cur.Val } func (this *MyLinkedList) AddAtHead(val int) { this.AddAtIndex(0, val) } func (this *MyLinkedList) AddAtTail(val int) { this.AddAtIndex(this.cnt, val) } func (this *MyLinkedList) AddAtIndex(index int, val int) { if index > this.cnt { return } pre := this.dummy for ; index > 0; index-- { pre = pre.Next } pre.Next = &ListNode{val, pre.Next} this.cnt++ } func (this *MyLinkedList) DeleteAtIndex(index int) { if index < 0 || index >= this.cnt { return } pre := this.dummy for ; index > 0; index-- { pre = pre.Next } t := pre.Next pre.Next = t.Next t.Next = nil this.cnt-- } /** * Your MyLinkedList object will be instantiated and called as such: * obj := Constructor(); * param_1 := obj.Get(index); * obj.AddAtHead(val); * obj.AddAtTail(val); * obj.AddAtIndex(index,val); * obj.DeleteAtIndex(index); */ ``` #### TypeScript ```ts class LinkNode { public val: number; public next: LinkNode; constructor(val: number, next: LinkNode = null) { this.val = val; this.next = next; } } class MyLinkedList { public head: LinkNode; constructor() { this.head = null; } get(index: number): number { if (this.head == null) { return -1; } let cur = this.head; let idxCur = 0; while (idxCur < index) { if (cur.next == null) { return -1; } cur = cur.next; idxCur++; } return cur.val; } addAtHead(val: number): void { this.head = new LinkNode(val, this.head); } addAtTail(val: number): void { const newNode = new LinkNode(val); if (this.head == null) { this.head = newNode; return; } let cur = this.head; while (cur.next != null) { cur = cur.next; } cur.next = newNode; } addAtIndex(index: number, val: number): void { if (index <= 0) { return this.addAtHead(val); } const dummy = new LinkNode(0, this.head); let cur = dummy; let idxCur = 0; while (idxCur < index) { if (cur.next == null) { return; } cur = cur.next; idxCur++; } cur.next = new LinkNode(val, cur.next || null); } deleteAtIndex(index: number): void { if (index == 0) { this.head = (this.head || {}).next; return; } const dummy = new LinkNode(0, this.head); let cur = dummy; let idxCur = 0; while (idxCur < index) { if (cur.next == null) { return; } cur = cur.next; idxCur++; } cur.next = (cur.next || {}).next; } } /** * Your MyLinkedList object will be instantiated and called as such: * var obj = new MyLinkedList() * var param_1 = obj.get(index) * obj.addAtHead(val) * obj.addAtTail(val) * obj.addAtIndex(index,val) * obj.deleteAtIndex(index) */ ``` #### Rust ```rust #[derive(Default)] struct MyLinkedList { head: Option>, } /** * `&self` means the method takes an immutable reference. * If you need a mutable reference, change it to `&mut self` instead. */ impl MyLinkedList { fn new() -> Self { Default::default() } fn get(&self, mut index: i32) -> i32 { if self.head.is_none() { return -1; } let mut cur = self.head.as_ref().unwrap(); while index > 0 { match cur.next { None => { return -1; } Some(ref next) => { cur = next; index -= 1; } } } cur.val } fn add_at_head(&mut self, val: i32) { self.head = Some(Box::new(ListNode { val, next: self.head.take(), })); } fn add_at_tail(&mut self, val: i32) { let new_node = Some(Box::new(ListNode { val, next: None })); if self.head.is_none() { self.head = new_node; return; } let mut cur = self.head.as_mut().unwrap(); while let Some(ref mut next) = cur.next { cur = next; } cur.next = new_node; } fn add_at_index(&mut self, mut index: i32, val: i32) { let mut dummy = Box::new(ListNode { val: 0, next: self.head.take(), }); let mut cur = &mut dummy; while index > 0 { if cur.next.is_none() { return; } cur = cur.next.as_mut().unwrap(); index -= 1; } cur.next = Some(Box::new(ListNode { val, next: cur.next.take(), })); self.head = dummy.next; } fn delete_at_index(&mut self, mut index: i32) { let mut dummy = Box::new(ListNode { val: 0, next: self.head.take(), }); let mut cur = &mut dummy; while index > 0 { if let Some(ref mut next) = cur.next { cur = next; } index -= 1; } cur.next = cur.next.take().and_then(|n| n.next); self.head = dummy.next; } } ``` ### 方法二:静态数组实现单链表 在方法一中,我们使用了指针引用的方式,每次动态创建一个链表节点。在链表节点数量达到 $10^5$ 甚至更大时,频繁执行 new 操作,会大大增加程序的执行耗时。 因此,我们可以使用静态数组来实现单链表,预先申请一块大小略大于数据范围的内存空间,每次插入节点时,从数组中取出一个空闲的位置,将新节点插入到该位置,同时更新该位置的前驱和后继节点的指针引用。 我们定义以下几个变量,其中: - `head` 存放链表头节点的索引,初始时指向 $-1$。 - `e` 存放链表所有节点的值(预先申请)。 - `ne` 存放链表所有节点的 `next` 指针(预先申请)。 - `idx` 指向当前可分配的节点索引,初始时指向索引 $0$。 - `cnt` 记录当前链表节点个数,初始时为 $0$。 具体操作可参考以下代码。时间复杂度与方法一相同。 #### Python3 ```python class MyLinkedList: def __init__(self): self.e = [0] * 1010 self.ne = [0] * 1010 self.idx = 0 self.head = -1 self.cnt = 0 def get(self, index: int) -> int: if index < 0 or index >= self.cnt: return -1 i = self.head for _ in range(index): i = self.ne[i] return self.e[i] def addAtHead(self, val: int) -> None: self.e[self.idx] = val self.ne[self.idx] = self.head self.head = self.idx self.idx += 1 self.cnt += 1 def addAtTail(self, val: int) -> None: self.addAtIndex(self.cnt, val) def addAtIndex(self, index: int, val: int) -> None: if index > self.cnt: return if index <= 0: self.addAtHead(val) return i = self.head for _ in range(index - 1): i = self.ne[i] self.e[self.idx] = val self.ne[self.idx] = self.ne[i] self.ne[i] = self.idx self.idx += 1 self.cnt += 1 def deleteAtIndex(self, index: int) -> None: if index < 0 or index >= self.cnt: return -1 self.cnt -= 1 if index == 0: self.head = self.ne[self.head] return i = self.head for _ in range(index - 1): i = self.ne[i] self.ne[i] = self.ne[self.ne[i]] # Your MyLinkedList object will be instantiated and called as such: # obj = MyLinkedList() # param_1 = obj.get(index) # obj.addAtHead(val) # obj.addAtTail(val) # obj.addAtIndex(index,val) # obj.deleteAtIndex(index) ``` #### Java ```java class MyLinkedList { private int[] e = new int[1010]; private int[] ne = new int[1010]; private int head = -1; private int idx; private int cnt; public MyLinkedList() { } public int get(int index) { if (index < 0 || index >= cnt) { return -1; } int i = head; while (index-- > 0) { i = ne[i]; } return e[i]; } public void addAtHead(int val) { e[idx] = val; ne[idx] = head; head = idx++; ++cnt; } public void addAtTail(int val) { addAtIndex(cnt, val); } public void addAtIndex(int index, int val) { if (index > cnt) { return; } if (index <= 0) { addAtHead(val); return; } int i = head; while (--index > 0) { i = ne[i]; } e[idx] = val; ne[idx] = ne[i]; ne[i] = idx++; ++cnt; } public void deleteAtIndex(int index) { if (index < 0 || index >= cnt) { return; } --cnt; if (index == 0) { head = ne[head]; return; } int i = head; while (--index > 0) { i = ne[i]; } ne[i] = ne[ne[i]]; } } /** * 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); */ ``` #### C++ ```cpp class MyLinkedList { private: int e[1010], ne[1010]; int head = -1, idx = 0, cnt = 0; public: MyLinkedList() { } int get(int index) { if (index < 0 || index >= cnt) { return -1; } int i = head; while (index--) { i = ne[i]; } return e[i]; } void addAtHead(int val) { e[idx] = val; ne[idx] = head; head = idx++; ++cnt; } void addAtTail(int val) { addAtIndex(cnt, val); } void addAtIndex(int index, int val) { if (index > cnt) { return; } if (index <= 0) { addAtHead(val); return; } int i = head; while (--index) { i = ne[i]; } e[idx] = val; ne[idx] = ne[i]; ne[i] = idx++; ++cnt; } void deleteAtIndex(int index) { if (index < 0 || index >= cnt) { return; } --cnt; if (index == 0) { head = ne[head]; return; } int i = head; while (--index) { i = ne[i]; } ne[i] = ne[ne[i]]; } }; /** * 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); */ ``` #### Go ```go type MyLinkedList struct { e []int ne []int idx int head int cnt int } func Constructor() MyLinkedList { e := make([]int, 1010) ne := make([]int, 1010) return MyLinkedList{e, ne, 0, -1, 0} } func (this *MyLinkedList) Get(index int) int { if index < 0 || index >= this.cnt { return -1 } i := this.head for ; index > 0; index-- { i = this.ne[i] } return this.e[i] } func (this *MyLinkedList) AddAtHead(val int) { this.e[this.idx] = val this.ne[this.idx] = this.head this.head = this.idx this.idx++ this.cnt++ } func (this *MyLinkedList) AddAtTail(val int) { this.AddAtIndex(this.cnt, val) } func (this *MyLinkedList) AddAtIndex(index int, val int) { if index > this.cnt { return } if index <= 0 { this.AddAtHead(val) return } i := this.head for ; index > 1; index-- { i = this.ne[i] } this.e[this.idx] = val this.ne[this.idx] = this.ne[i] this.ne[i] = this.idx this.idx++ this.cnt++ } func (this *MyLinkedList) DeleteAtIndex(index int) { if index < 0 || index >= this.cnt { return } this.cnt-- if index == 0 { this.head = this.ne[this.head] return } i := this.head for ; index > 1; index-- { i = this.ne[i] } this.ne[i] = this.ne[this.ne[i]] } /** * Your MyLinkedList object will be instantiated and called as such: * obj := Constructor(); * param_1 := obj.Get(index); * obj.AddAtHead(val); * obj.AddAtTail(val); * obj.AddAtIndex(index,val); * obj.DeleteAtIndex(index); */ ``` #### TypeScript ```ts class MyLinkedList { e: Array; ne: Array; idx: number; head: number; cnt: number; constructor() { this.e = new Array(1010).fill(0); this.ne = new Array(1010).fill(0); this.head = -1; this.idx = 0; this.cnt = 0; } get(index: number): number { if (index < 0 || index >= this.cnt) { return -1; } let i = this.head; while (index--) { i = this.ne[i]; } return this.e[i]; } addAtHead(val: number): void { this.e[this.idx] = val; this.ne[this.idx] = this.head; this.head = this.idx++; this.cnt++; } addAtTail(val: number): void { this.addAtIndex(this.cnt, val); } addAtIndex(index: number, val: number): void { if (index > this.cnt) { return; } if (index <= 0) { this.addAtHead(val); return; } let i = this.head; while (--index) { i = this.ne[i]; } this.e[this.idx] = val; this.ne[this.idx] = this.ne[i]; this.ne[i] = this.idx++; this.cnt++; } deleteAtIndex(index: number): void { if (index < 0 || index >= this.cnt) { return; } this.cnt--; if (index == 0) { this.head = this.ne[this.head]; return; } let i = this.head; while (--index) { i = this.ne[i]; } this.ne[i] = this.ne[this.ne[i]]; } } /** * Your MyLinkedList object will be instantiated and called as such: * var obj = new MyLinkedList() * var param_1 = obj.get(index) * obj.addAtHead(val) * obj.addAtTail(val) * obj.addAtIndex(index,val) * obj.deleteAtIndex(index) */ ```