链表
基本概念
链表(Linked List)是用于存储元素的一种基本数据结构。它经常被拿来与数组做比较,链表中的元素不是顺序存储的,而是通过指针连接在一起。
链表中的每个元素被称为节点。每个节点通常由两部分组成:数据部分和指针部分。数据部分存储元素的值,而指针部分存储指向下一个节点的引用。链表的第一个节点称为头节点。我们通常使用头节点来代表整个链表。链表的最后一个节点称为尾节点。尾节点的指针部分通常指向 None,表示链表的结束。
基本操作
链表最基本的操作包括:
- 创建一个空链表
- 插入新节点。链表没有索引,所以在插入新节点的时候,通常会用一个已有节点做参考,比如在给定节点的前面或后面插入新节点;或者在链表头或尾插入新节点。
- 删除给定节点
- 遍历和查找
下面的程序实现了这几个最基本操作:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data): # 将一个新节点添加到链表的末尾
new_node = Node(data) # 创建一个新节点
if not self.head: # 空链表
self.head = new_node
return
last_node = self.head
while last_node.next: # 找到链表尾
last_node = last_node.next
last_node.next = new_node # 链接到新节点
def print_list(self):
cur_node = self.head
while cur_node: # 遍历每个节点
print(cur_node.data, end=" -> ")
cur_node = cur_node.next
print("None")
def insert_after_node(self, prev_node, data):
if not prev_node:
print("Previous node is not in the list")
return
new_node = Node(data)
new_node.next = prev_node.next # 新节点的指针指向参考节点的下一个节点
prev_node.next = new_node # 参考节点的指针指向新节点
def find_node_by_key(self, key):
cur_node = self.head
while cur_node: # 遍历所有节点,一一比较
if cur_node.data == key: # 直到找到目标节点
return cur_node
cur_node = cur_node.next
return None
def delete_node(self, node_to_delete):
if not node_to_delete:
return
# 如果被删除的是头结点
if self.head == node_to_delete:
self.head = self.head.next
return
prev_node = None
cur_node = self.head
while cur_node and cur_node != node_to_delete:
prev_node = cur_node # 找到前面的节点
cur_node = cur_node.next
# 如果节点不在链表里则直接返回
if not cur_node:
return
# 把当前节点从链表中移除
prev_node.next = cur_node.next
# 使用链表
llist = LinkedList()
llist.append(1)
llist.append(2)
llist.append(3)
llist.print_list() # 输出: 1 -> 2 -> 3 -> None
node_to_delete = llist.find_node_by_key(2)
llist.delete_node(node_to_delete)
llist.print_list() # 1 -> 3 -> None
在上面的程序中,Node 类用于表示链表中的每一个节点。每个节点都有两个属性:data (用于存储值) 和 next (指向下一个节点的引用)。
LinkedList 类用于表示整个链表。它包含一个属性 head,指向链表的第一个节点。append() 方法将一个新节点添加到链表的末尾。insert_after_node() 方法可以在给定的 prev_node 后面插入一个新节点。 find_node_by_key() 方法可以根据数据找到一个节点。delete_node() 方法则可以删除一个节点。
从上面的示例的实现就可以看出来,在队列中插入数据的时间复杂度是 ,因为插入操作不需要挪动任何其它元素。但是查找一个节点时间复杂度是 ,链表不能做索引,只能一个一个节点查看。
上面的链表中,删除节点的时间复杂度是 ,因为只有找到当前节点的上一个节点之后才能删除。如果有一个函数,是删除下一个节点,那就不需要遍历整个链表了,时间复杂度可以降低到 。或者,如果是在双向链表中,删除节点的时间复杂度也可以是 。