同步发布在github.io博客
1. 数组和列表(线性结构)
线性结构特点:
- 内存连续
- 下标访问
数组和列表都属于线性结构。
1.1 数组
python内置的array:只能存同一数据类型(数值、字符)
1.2 列表
操作 | 平均时间复杂度 |
---|---|
list[index] | O(1) |
list.append | O(1) |
list.insert | O(n) |
list.pop(index),default last element | O(1) |
list.remove | O(n) |
1.3 用list实现Array ADT
ADT: Abstact Data Type, 抽象数据类型,在组合已有的数据结构来实现一种新的数据类型中,ADT定义了类型的数据和操作。python内置的list可以看成一种抽象数据类型。
class Array(object):
def __init__(self,size=32):
self._size = size
self._items = [None]*size
def __getitem__(self,index):
return self._items[index]
def __setitem__(self,index,value):
self._items[index] = value
def __len__(self):
return self._size
def clear(self, value=None):
for i in range(len(self._items)):
self._items[i] = value
def __iter__(self):
for item in self._items:
yield item
详细代码见github:array_and_list.py
2. 链表(链式结构)
链式结构与线性结构区别:
- 内存不连续(因此需要每个链接表的节点保存一个指向下一个节点的指针)
- 不能通过下标随机访问
- 查找元素需要从头遍历
2.1 单链表
2.1.1 时间复杂度
链表操作 | 平均时间复杂度 |
---|---|
linked_list.append(value) | O(1) |
linked_list.appendleft(value) | O(1) |
linked_list.find(value) | O(n) |
linked_list.remove(value) | O(n) |
2.1.2 实现
定义一个单链表的节点:
class Node(object):
"""
一个链接表的节点,有两个属性:值;保存下一个节点的位置。
"""
def __init__(self, value=None, next=None):
self.value = None
self.next = None
单链表 LinkedList ADT:
class LinkedList(object):
""" 链接表 ADT
[root] -> [node0] -> [node1] -> [node2]
"""
def __init__(self, maxsize=None):
"""
:param maxsize: int or None, 如果是None, 无限扩充
"""
self.maxsize = None
self.root = Node() # 默认root节点指向None
self.tailnode = None
self.length = 0
def __len__(self):
return self.length
def append(self, value):
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value) # 构造节点
tailnode = self.tailnode
if tailnode is None: # length=0时,追加到root后
self.root.next = node
else:
tailnode.next = node
self.tailnode = node # 更新tailnode
self.length += 1
def appendleft(self, value):
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value)
if self.tailnode is None:
self.tailnode = node
headnode = self.root.next
self.root.next = node
node.next = headnode
self.length += 1
def iter_node(self):
"""
遍历从head节点到tail节点
"""
curnode = self.root.next
while curnode is not self.tailnode: # 从第一个节点开始遍历
yield curnode
curnode = curnode.next # 移动到下一个节点
if curnode is not None:
yield curnode
def __iter__(self):
for node in self.iter_node():
yield node.value
def remove(self, value):
"""
删除包含值的一个节点,将其前一个节点的next指向被查询节点的下一个
"""
prevnode = self.root
for curnode in self.iter_node():
if curnode.value == value:
prevnode.next = curnode.next
if curnode is self.tailnode:
self.tailnode = prevnode # 更新tailnode
del curnode
self.length -= 1
return 1 # 删除成功
else:
prevnode = curnode
return -1 # 表明删除失败
def find(self, value):
"""
查找一个节点,返回序号,从0开始
"""
index = 0
for node in self.iter_node():
if node.value == value:
return index # 找到
index += 1
return -1 # 没找到
def popleft(self):
"""
删除第一个链表节点
"""
if self.root.next is None:
raise Exception('pop from empty LinkedList')
headnode = self.root.next
self.root.next = headnode.next
self.length -= 1
value = headnode.value
if self.tailnode is headnode: # 单节点删除 tailnode处理
self.tailnode = None
del headnode
return value
def insert(self, index, value):
"""
在指定位置插入节点
"""
if self.root == None:
return
if index <= 0: # 等于在链表头部插入一个元素
self.appendleft(value)
elif index >= len(self) : #等于在链表尾部插入一个元素
self.append(value)
else: # 在链表中间插入
node = Node(value)
count = 0
prevnode = self.root
for curnode in self.iter_node():
if count == index:
prevnode.next = node
node.next = curnode
self.length += 1
else:
prevnode = curnode
count += 1
def replace(self, value, new_value):
"""
替换节点的值
"""
index = 0
for node in self.iter_node():
if node.value == value:
node.value = new_value
return index
index += 1
return -1 #没有找到该值
def clear(self):
for node in self.iter_node():
del node
self.root.next = None
self.length = 0
self.tailnode = None
详细代码见github:linked_list.py
2.2 双链表
2.2.1 与单链表的区别
单链表的缺点:find和remove都是O(n),因为无论是查找还是删除,都需要先查找,而单链表的查找只能从头顺着找到尾。
相比于单链表,双链表每个节点既保存了指向下一个节点的指针,也保存了上一个节点的指针。优势如下:
- 将root的prev指向tail节点,就能将整个串起来形成循环
- 通过告诉某个节点,删除该节点,复杂度为O(1)(PS:如果给的是值,还是需要先查找,O(n))
2.2.2 时间复杂度
循环双链表操作 | 平均时间复杂度 |
---|---|
cdll.append(value) | O(1) |
cdll.appendleft(value) | O(1) |
cdll.remove(node),注意这里参数是node | O(1) |
cdll.headnode() | O(1) |
cdll.tailnode() | O(1) |
2.2.3 实现
定义一个双链表的节点:
class Node(object):
def __init__(self, value=None, prev=None, next=None):
self.value, self.prev, self.next = value, prev, next
双链表 CircularDoubleLinkedList ADT:
class CircularDoubleLinkedList(object):
"""
循环双链表 ADT
所谓循环,就是通过root的prev指向tail节点将整个串起来
"""
def __init__(self, maxsize=None):
self.maxsize = maxsize
node = Node()
node.next, node.prev = node, node
self.root = node
self.length = 0
def __len__(self):
return self.length
def headnode(self):
return self.root.next
def tailnode(self):
return self.root.prev
def append(self,value): #O(1)
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value=value)
tailnode = self.tailnode() or self.root
tailnode.next = node
node.prev = tailnode
node.next = self.root
self.root.prev = node
self.length += 1
def appendleft(self, value):
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value=value)
if self.root.next is self.root: # empty的情况
node.next = self.root
node.prev = self.root
self.root.next = node
self.root.prev = node
else:
node.prev = self.root
headnode = self.root.next
node.next = headnode
headnode.prev = node
self.root.next = node
self.length += 1
def remove(self, node): # 传入参数为node时为O(1)
if node is self.root:
return
else:
node.prev.next = node.next
node.next.prev = node.prev
self.length -= 1
return node
def iter_node(self):
if self.root.next is self.root:
return
curnode = self.root.next
while curnode.next is not self.root:
yield curnode
curnode = curnode.next
yield curnode
def __iter__(self):
for node in self.iter_node():
yield node.value
def iter_node_reverse(self):
"""
区别于单链表的反序遍历
"""
if self.root.prev is self.root:
return
curnode = self.root.prev
while curnode.prev is not self.root:
yield curnode
curnode = curnode.prev
yield curnode
详细代码见github:double_link_list.py