单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。
- 表元素域elem用来存放具体的数据。
- 链接域next用来存放下一个节点的位置(python中的标识)
- 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
# 节点, 包括:元素和下一个节点的地址 class Node(object): def __init__(self, elem): self.elem = elem self.next = None class SingleLinkLise(object): '''单链表''' def __init__(self, node=None): self._head = node def is_empty(self): '''链表是否为空''' return self._head == None def length(self): '''链表长度''' count = 0 cur = self._head while cur != None: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' cur = self._head while cur != None: print(cur.elem, end=' ') cur = cur.next
头部添加元素
def add(self, item): '''链表头部添加元素''' node = Node(item) # 创建一个节点 # 将新节点next指向self._head指向的节点 node.next = self._head # self._head节点再指向新的节点 self._head = node def append(self, item): '''链表尾部添加元素''' node = Node(item) # 判断是否为空,如果为空则直接指向新节点 if self.is_empty(): self._head = node # 如果不为空,则找到最后一个节点添加 else: cur = self._head while cur.next != None: cur = cur.next cur.next = node
指定位置添加元素
def insert(self, pos, item): '''指定位置添加元素''' cur = self._head count = 0 # 如果位置在第一个元素之前,就往头添加 if pos <= 0: self.add(item) # 如果位置在最后一个元素之后,就往尾添加 elif pos > self.length() - 1: self.append(item) else: while count < pos -1: count += 1 cur = cur.next node = Node(item) # 先将新节点node的next指向插入位置的节点 node.next = cur.next # 将插入位置的前一个节点的next指向新节点 cur.next = node
删除节点
def remove(self, item): '''删除节点''' cur = self._head pre = None while cur != None: if cur.elem == item: # 如果是头节点 if not pre: self._head = cur.next else: # 将删除位置的上一个节点next指向删除位置的下一个节点 pre.next = cur.next break else: pre = cur cur = cur.next def search(self, item): '''查找节点是否存在''' cur = self._head count = 0 while cur != None: if cur.elem == item: return count else: cur = cur.next count += 1 return -1 # 测试 if __name__ == '__main__': sll = SingleLinkLise() # 判断是否为空 print(sll.is_empty()) # 获取链表长度 print(sll.length()) # 遍历链表 sll.travel() # 头添加元素 sll.add(1) sll.add(100) print(sll.is_empty()) print(sll.length()) sll.travel() print() # 尾添加元素 sll.append(300) sll.append(400) sll.travel() print() # 指定位置添加元素 sll.insert(3, 200) sll.insert(6, 500) sll.travel() print() # 删除节点 sll.remove(500) sll.travel() print() # 查找节点 print(sll.search(500))
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
操作 | 链表 | 顺序表 |
---|---|---|
访问元素 | O(n) | O(1) |
在头部插入/删除 | O(1) | O(n) |
在尾部插入/删除 | O(n) | O(1) |
在中间插入/删除 | O(n) | O(n) |
注意虽然表面看起来复杂度都是 O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前后移位操作,只能通过拷贝和覆盖的方法进行。