我们在 Python 序列:列表 (list),元组(tuple),字符串(str)深入分析(包括扩容和摊销)。 和 Python:栈和队列的 python 列表实现 中可以看出列表是存在一定问题的:
于是就有了另外一种结构,就是链表。链表和列表可以对其中的元素保持一定的顺序,但是实现方式不同:
class Node:
def __init__(self, val):
self.val = val
self.next = None
class SingleLinkedList:
def __init__(self, node=None):
self.head = node
if node:
self.size = 1
else:
self.size = 0
def is_empty(self):
return self.size == 0
def length(self):
return self.size
def add_first(self, val):
node = Node(val)
node.next = self.head
self.head = node
self.size += 1
def append(self, val):
node = Node(val)
current = self.head
if current is None:
self.head = node
else:
while current.next:
current = current.next
current.next = node
self.size += 1
def insert_by_position(self, pos, val):
if pos > self.size or pos < 0:
print('Out of linked list index!')
elif pos == 0:
self.add_first(val)
else:
node = Node(val)
count = 0
pre = self.head
while count < (pos-1):
pre = pre.next
count = count + 1
node.next = pre.next
pre.next = node
self.size += 1
def remove_by_value(self, val):
current = self.head
pre = None
while current:
if current.val == val:
if pre:
pre.next = current.next
else:
self.head = current.next
self.size -= 1
return
pre = current
current = current.next
print('Not found value!')
def search(self, val):
current = self.head
while current:
if current.val == val:
return True
else:
current = current.next
return False
def travel(self):
current = self.head
while current:
print(current.val, end=' ')
current = current.next
print()
if __name__ == '__main__':
pass
class Node:
def __init__(self, val):
self.val = val
self.next = None
定义我们的节点,节点中 val 为本节点的值,next 指向下一个节点(如果没有则为空)
class SingleLinkedList:
def __init__(self, node=None):
self.head = node
if node:
self.size = 1
else:
self.size = 0
初始化我们的单链表,简单的提供了两种初始化方法,一种是空的初始化,一种使用一个节点初始化。为了直接返回链表的长度,不必遍历整个链表,因此保存了一个链表长度的变量,时间复杂度为 O(1)。
注意:如果没有 size 变量,那么求链表的长度的时间复杂度为 O(n),因为我们只有链表的头结点,我们不知道尾节点(当然你也可以用一个变量来保存,每次操作的时候要考虑尾节点是否变化),因此只能根据头结点的 next 来遍历整个链表,然后计数才能知道链表的长度。
我们实现的方法有:
方法 | 操作 | 时间复杂度 |
---|---|---|
is_empty | 判断是否为空链表 | O(1) |
length | 链表长度 | O(1) |
add_first | 在头部添加元素 | O(1) |
append | 在尾部添加元素 | O(n) |
insert_by_position | 按位置插入 | O(k) |
remove_by_value | 删除链表中包含改值的第一个节点 | O(k) |
search | 查找某元素 | O(k) |
travel | 遍历打印元素 | O(n) |
其中 k 是第几个节点或位置满足条件。
注意:我们的实现中 append 的复杂度是 O(n),这是为了和一般的链表实现一致,只有头结点,一个简单的方法降为 O(1) 的操作是,链表中用一个变量保存尾节点的引用。这样只需要尾节点的 next 指向新加入的节点即可。
在上面中,我们发现删除节点:
def remove_by_value(self, val):
current = self.head
pre = None
while current:
if current.val == val:
if pre:
pre.next = current.next
else:
self.head = current.next
self.size -= 1
return
pre = current
current = current.next
print('Not found value!')
需要维护一个 pre 变量指向此元素的上一个元素,因为每个节点只有 next 指向下一节点,并没有指向前一节点,删除的时候要修改上一指针的 next 指向。所以添加了一个辅助节点来指向当前节点的上一节点,这个方法在链表中很常用,这个节点一般叫哨兵节点。为了可以方便的访问之前和之后节点,于是有了双向链表。
class Node:
def __init__(self, val):
self.val = val
self.next = None
self.pre = None
class DoubleLinkedList:
def __init__(self, node=None):
self.head = node
if node:
self.size = 1
else:
self.size = 0
def is_empty(self):
return self.size == 0
def length(self):
return self.size
def travel(self):
current = self.head
while current:
print(current.val, end=' ')
current = current.next
print()
def add_first(self, val):
node = Node(val)
current = self.head
if current:
node.next = current
current.pre = node
self.head = node
else:
self.head = node
self.size += 1
def append(self, val):
node = Node(val)
current = self.head
if self.head:
while current.next:
current = current.next
current.next = node
node.pre = current
else:
self.head = node
self.size += 1
def insert_by_position(self, pos, val):
if pos < 0 or pos > self.size:
print('Out of linked list index!')
elif pos == 0:
self.add_first(val)
else:
count = 0
node = Node(val)
current = self.head
while current < pos - 1:
current = current.next
count += 1
node.next = current.next
node.pre = current
current.next.pre = node
current.next = node
self.size += 1
def remove_by_value(self, val):
current = self.head
if current:
if current.val == val:
if current.next is None:
self.head = None
else:
current.next.pre = None
self.head = current.next
self.size -= 1
return
while current:
if current.val == val:
current.pre.next = current.next
current.next.pre = current.pre
self.size -= 1
return
else:
current = current.next
print('Not found value!')
else:
print('Not found value!')
def search(self, val):
current = self.head
while current:
if current.val == val:
return True
else:
current = current.next
return False
if __name__ == "__main__":
pass
class Node:
def __init__(self, val):
self.val = val
self.next = None
self.pre = None
双向链表的节点相对于担心链表的节点多了一个 pre 变量指向上一节点(如果是头结点,则为空)。
class Node:
def __init__(self, val):
self.val = val
self.next = None
class SingleLinkedList:
def __init__(self, node=None):
self.head = node
if node:
self.size = 1
else:
self.size = 0
def is_empty(self):
return self.size == 0
def length(self):
return self.size
def add_first(self, val):
node = Node(val)
if self.is_empty():
self.head = node
node.next = self.head
else:
node.next = self.head
current = self.head
while current.next != self.head:
current = current.next
current.next = node
self.head = node
self.size += 1
def append(self, val):
node = Node(val)
current = self.head
if current is None:
self.head = node
node.next = self.head
else:
while current.next != self.head:
current = current.next
current.next = node
node.next = self.head
self.size += 1
def insert_by_position(self, pos, val):
if pos > self.size or pos < 0:
print('Out of linked list index!')
elif pos == 0:
self.add_first(val)
else:
node = Node(val)
count = 0
pre = self.head
while count < (pos-1):
pre = pre.next
count = count + 1
node.next = pre.next
pre.next = node
self.size += 1
def remove_by_value(self, val):
if self.is_empty():
print('Not found value!')
return
current = self.head
pre = None
if current.val == val:
if current.next != self.head:
while current.next != self.head:
current = current.next
current.next = self.head.next
self.head = self.head.next
else:
self.head = None
self.size -= 1
else:
pre = self.head
while current.next != self.head:
if current.val == val:
pre.next = current.next
self.size -= 1
return
else:
pre = current
current = current.next
if current.val == val:
pre.next = current.next
self.size -= 1
print('Not found value!')
def search(self, val):
if self.is_empty():
return False
current = self.head
if current.val == val:
return True
while current.next != self.head:
current = current.next
if current.val == val:
return True
return False
def travel(self):
current = self.head
for _ in range(self.size):
print(current.val, end=' ')
current = current.next
print()
if __name__ == '__main__':
pass
循环链表值得是链表尾节点的 next 不是指向 None,而是指向头结点。最大的改变就是遍历的时候,尾节点的判断条件不是 next == None,而是 next == self.head。另外也可以加一个计数变量来比较和链表长度的关系。