目录
一、简介 - 双链表
// C++ implementation
// Definition for doubly-linked list.
struct DoublyListNode {
int val;
DoublyListNode *next, *prev;
DoublyListNode(int x) : val(x), next(NULL), prev(NULL) {}
};
# Python implementation
# Definition for doubly-linked list.
class DoublyListNode:
def __init__(self, val):
self.val = val
self.prev = None
self.next = None
参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/756/
二、添加操作 - 双链表
参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/757/
三、删除操作 - 双链表
参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/758/
四、设计链表
4.1 题目要求
4.2 解决过程
面试要点
原理简述
class MyLinkedList:
def __init__(self):
self.size = 0
# sentinel nodes as pseudo-head and pseudo-tail
self.head, self.tail = ListNode(0), ListNode(0)
self.head.next = self.tail
self.tail.prev = self.head
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
self.prev = None
# 伪代码
new.prev = pred
new.next = succ
pred.next = new
succ.prev = new
# 伪代码
pred.next = succ
succ.prev = pred
# 伪代码
# choose the fastest way: to move from the head or to move from the tail
if index + 1 < self.size - index:
curr = self.head
for _ in range(index + 1):
curr = curr.next
else:
curr = self.tail
for _ in range(self.size - index):
curr = curr.prev
官方实现
class ListNode:
def __init__(self, x):
self.val = x
self.next, self.prev = None, None
class MyLinkedList:
def __init__(self):
self.size = 0
# sentinel nodes as pseudo-head and pseudo-tail
self.head, self.tail = ListNode(0), ListNode(0)
self.head.next = self.tail
self.tail.prev = self.head
def get(self, index: int) -> int:
"""
Get the value of the index-th node in the linked list. If the index is invalid, return -1.
"""
# if index is invalid
if index < 0 or index >= self.size:
return -1
# choose the fastest way: to move from the head
# or to move from the tail
if index + 1 < self.size - index:
curr = self.head
for _ in range(index + 1):
curr = curr.next
else:
curr = self.tail
for _ in range(self.size - index):
curr = curr.prev
return curr.val
def addAtHead(self, val: int) -> None:
"""
Add a node of value val before the first element of the linked list.
After the insertion, the new node will be the first node of the linked list.
"""
pred, succ = self.head, self.head.next
self.size += 1
to_add = ListNode(val)
to_add.prev = pred
to_add.next = succ
pred.next = to_add
succ.prev = to_add
def addAtTail(self, val: int) -> None:
"""
Append a node of value val to the last element of the linked list.
"""
succ, pred = self.tail, self.tail.prev
self.size += 1
to_add = ListNode(val)
to_add.prev = pred
to_add.next = succ
pred.next = to_add
succ.prev = to_add
def addAtIndex(self, index: int, val: int) -> None:
"""
Add a node of value val before the index-th node in the linked list.
If index equals to the length of linked list,
the node will be appended to the end of linked list.
If index is greater than the length, the node will not be inserted.
"""
# If index is greater than the length, the node will not be inserted.
if index > self.size:
return
# [so weird] If index is negative, the node will be inserted at the head of the list.
if index < 0:
index = 0
# find predecessor and successor of the node to be added
if index < self.size - index:
pred = self.head
for _ in range(index):
pred = pred.next
succ = pred.next
else:
succ = self.tail
for _ in range(self.size - index):
succ = succ.prev
pred = succ.prev
# insertion itself
self.size += 1
to_add = ListNode(val)
to_add.prev = pred
to_add.next = succ
pred.next = to_add
succ.prev = to_add
def deleteAtIndex(self, index: int) -> None:
"""
Delete the index-th node in the linked list, if the index is valid.
"""
# if the index is invalid, do nothing
if index < 0 or index >= self.size:
return
# find predecessor and successor of the node to be deleted
if index < self.size - index:
pred = self.head
for _ in range(index):
pred = pred.next
succ = pred.next.next
else:
succ = self.tail
for _ in range(self.size - index - 1):
succ = succ.prev
pred = succ.prev.prev
# delete pred.next
self.size -= 1
pred.next = succ
succ.prev = pred
优化实现
此处对双链表进行 类内嵌、方法封装、成员非公有化 等优化处理。例如,创建一个轻量级非公有 _ListNode 类,由于该类不会直接暴露给用户,所以被定义为非公有的、最终的 MyLinkedList 类的内部嵌套类;各种属性成员和成员函数也被同理被定义为非公有的;为提高内存的利用率,专门定义了 __slots__ 方法,因为一个链表中通常具有若干个节点实例。实现如下:
2020/07/08 - 94.35%
## 个人特别实现 (未完)
class MyLinkedList:
""" An implementation of doubly linked list """
# -------------------------- nested _ListNode class -------------------------- #
class _ListNode:
""" Lightweight, nonpublic class for storing a doubly linked node """
__slots__ = "_val", "_next", "_prev" # straemline memory usage (special)
def __init__(self, elem=0, nxt=None, pre=None): # initialize node's fields
self._val = elem # reference to user's element
self._next = nxt # reference to the next node
self._prev = pre # reference to the previous node
# ------------------------- linked list basic methods ------------------------ #
def __init__(self):
""" initialize with five nonpublic properties """
self._size = 0 # the number of elements
self._head = self._ListNode() # sentinel node as pseudo-head
self._tail = self._ListNode() # sentinel node as pseudo-tail
self._head._next = self._tail # cross reference →
self._tail._prev = self._head # cross reference ←
def __len__(self):
""" return the length of the doubly linked list """
return self._size
def isEmpty(self):
""" return if the doubly linked list is empty """
return self._size == 0
# ----------------------- linked list nonpublic methods ---------------------- #
def _findNode(self, left_step, right_step):
""" find the specified node with the fastest starting point and direction """
if (left_step) < (right_step): # choose the fastest way
curr = self._head # close to left, move from the head →
for _ in range(left_step):
curr = curr._next
else:
curr = self._tail # close to right, move from the tail ←
for _ in range(right_step):
curr = curr._prev
return curr
def _insertNode(self, val, pred, succ):
""" insert a specific node with three parameters """
new_node = self._ListNode(val) # initialize the new node
new_node._prev = pred # cross reference
new_node._next = succ
pred._next = new_node
succ._prev = new_node
self._size += 1 # increment the node count
def _deleteNode(self, pred, succ):
""" delete a specific node with two parameters """
pred._next = succ # skipping cross reference
succ._prev = pred
self._size -= 1 # decrement the node count
# ------------------------ linked list public methods ------------------------ #
def get(self, index: int) -> int:
"""
Get the value of the index-th node in the linked list.
If the index is invalid, return -1.
"""
if (index < 0) or (index >= self._size): # return -1 if index is invalid
return -1
curr = self._findNode(index+1, self._size-index) # find the specified node
return curr._val
def addAtHead(self, val: int) -> None:
"""
Add a node of value val before the first element of the linked list.
After the insertion, the new node will be the first node of the linked list.
"""
pred, succ = self._head, self._head._next # head node's predecessor and sucessor
self._insertNode(val, pred, succ)
def addAtTail(self, val: int) -> None:
"""
Append a node of value val to the last element of the linked list.
"""
succ, pred = self._tail, self._tail._prev # tail node's sucessor and predecessor
self._insertNode(val, pred, succ)
def addAtIndex(self, index: int, val: int) -> None:
"""
Add a node of value val before the index-th node in the linked list.
If index equals to the length of linked list,
the node will be appended to the end of linked list.
If index is greater than the length,
the node will not be inserted.
"""
if index > self._size:
return
if index < 0:
index = 0
# find predecessor and successor of the node to be added
if index < (self._size-index):
pred = self._head # close to left, move from the head →
for _ in range(index):
pred = pred._next
succ = pred._next
else:
succ = self._tail # close to right, move from the tail ←
for _ in range(self._size-index):
succ = succ._prev
pred = succ._prev
self._insertNode(val, pred, succ) # insert itself
def deleteAtIndex(self, index: int) -> None:
"""
Delete the index-th node in the linked list, if the index is valid.
"""
if (index < 0) or (index >= self._size): # if the index is invalid, do nothing
return
# find predecessor and successor of the node to be deleted
if index < (self._size-index):
pred = self._head # close to left, move from the head →
for _ in range(index):
pred = pred._next
succ = pred._next._next
else:
succ = self._tail # close to right, move from the tail ←
for _ in range(self._size-index-1):
succ = succ._prev
pred = succ._prev._prev
self._deleteNode(pred, succ) # delete the node
注意,其中还加入了一些其他没用到但有意义的方法。从结果来看,几乎比单链表快 2 倍,可见 含哨兵节点的双链表 及其各种优化是有意义的。此外,还可以增加 反转 (reverse)、遍历 (traverse)、更新 (update)、排序 (sort) 等更多功能。
复杂度分析
参考文献
https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/759/