Python模拟链表
·
本文链表维护了 head、tail、size 三个属性,功能包含增、删(删除单个和删除多个)、查、反转、清除、重写魔法方法等。详见下文代码块
后续实现企业级:哨兵节点(dummy head)
链表实现
# 节点类
class ListNode:
# 每一个单个的节点初始化的时候数值域必须有值,指针域为None
def __init__(self, value):
# 数值域
self.value = value
# 指针域
self.next = None
# 链表类
class LinkedList:
"""
初始化链表类,链表类我们设置三个对象属性 头:head 尾:tail 大小:size
注意在操作链表时需要维护这三个属性:
让 head 始终指向链表的第一个元素,
tail 始终指向链表的最后一个元素,
size 始终是当前更改后链表的大小
"""
def __init__(self, node = None):
self.head = node
self.tail = node
self.size = 0
# 节点存在 需要遍历
if node:
self.size = 1
# 这里注意一个细节:
# while判断cur.next是否是空 最后cur获取到的就是最后一个节点
# while判断cur是否是空 最后cur获取到的就是None
cur = node
while cur.next:
self.size += 1
cur = cur.next
self.tail = cur
# 判空
def is_empty(self):
return self.head is None
# 获取链表所有的值
def get_values(self):
node_values = []
cur = self.head
while cur:
node_values.append(cur.value)
cur = cur.next
return node_values
# 头部插入:注意要维护head、size、tail
def prepend(self, value):
new_node = ListNode(value)
# 空链表需要维护tail
if self.head is None:
self.tail = new_node
else:
# 新插入节点的指针域 指向旧头节点
new_node.next = self.head
# 头指向新节点
self.head = new_node
# 维护链表大小
self.size += 1
# 尾部插入:注意要维护head、size、tail
def append(self, value):
new_node = ListNode(value)
if self.head is None:
# 空
self.head = new_node
else:
# 非空
self.tail.next = new_node # 旧尾节点挂上新节点
self.tail = new_node # 更新尾指针为新节点
self.size += 1
# 根据值删除单个节点:注意要维护head、size、tail
def remove(self,value):
# 空
if self.is_empty():
return False
# 情况1: 删除的是头节点
if self.head.value == value:
# 如果链表只有一个节点 维护tail
if self.size == 1:
self.head = None
self.tail = None
else:
self.head = self.head.next
self.size -= 1
return True
# 情况2:删除的是中间节点/尾节点
prev = self.head
cur = self.head.next
while cur:
# 找到了
if cur.value == value:
prev.next = cur.next
self.size -= 1
# 删除的是尾节点
if cur.next is None:
self.tail = prev
return True
# 未找到 后移
prev = cur
cur = cur.next
return False
# 根据值删除所有重复节点: 注意要维护head、size、tail
def remove_all(self,value):
del_count = 0
# 空
if self.is_empty():
return del_count
# 情况1: 批量删除头部所有匹配节点
while self.head and self.head.value == value:
self.head = self.head.next
self.size -= 1
del_count += 1
# 头部删空,同步清空尾指针
if self.is_empty():
self.tail = None
return del_count
# 情况2:删除的是中间节点/尾节点
prev = self.head
cur = self.head.next
while cur:
# 找到了
if cur.value == value:
prev.next = cur.next
del_count += 1
self.size -= 1
# 删除的是尾节点
if cur.next is None:
self.tail = prev
cur = prev.next
else:
# 未找到 后移
prev = cur
cur = cur.next
return del_count
# 在指定位置插入元素:注意要维护head、size、tail
def insert(self, index, value):
# 判断是否越界
if index < 0 or index > self.size:
raise IndexError('index out of range')
# 在开始节点插入
if index == 0:
self.prepend(value)
return
# 在结束节点插入
if index == self.size:
self.append(value)
return
# 在中间插入
new_node = ListNode(value)
cur = self.head
for _ in range(index - 1):
cur = cur.next
new_node.next = cur.next
cur.next = new_node
self.size += 1
# 链表反转:注意要维护head、tail
def reverse(self):
if self.is_empty() or self.size == 1:
return self
# 双指针
prev = None
cur = self.head
old_first_node = self.head
while cur:
temp = cur.next
cur.next = prev
# 后移
prev = cur
cur = temp
self.head = prev
self.tail = old_first_node
return self
# 根据值查找元素是否存在
def search(self, value):
cur = self.head
while cur:
if cur.value == value:
return True
cur = cur.next
return False
# 清除链表
def clear(self):
self.head = None
self.tail = None
self.size = 0
# 重写len魔法方法
def __len__(self):
return self.size
# 重写str魔法方法 方法中的map和join方法详细介绍见结尾扩展知识
def __str__(self):
# 获取链表中所有的值
node_values = self.get_values()
# 1. map(str, val_list):迭代器,把每个数字转字符串 必须要转,否则抛TypeError
node_str_values = map(str, node_values)
# 2. "->".join(迭代器):直接遍历迭代器拼接,无需转列表
return '->'.join(node_str_values)
# 迭代器
def __iter__(self):
cur = self.head
while cur:
yield cur.value
cur = cur.next
if __name__ == '__main__':
node1 = ListNode(2)
node2 = ListNode(2)
node3 = ListNode(1)
node4 = ListNode(2)
node5 = ListNode(3)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
ll = LinkedList(node1)
print("初始\t", ll, "长度", len(ll))
ll.remove(2)
print("删第一个2\t", ll, "长度", len(ll))
ll.remove_all(2)
print("删除全部2\t", ll, "长度", len(ll))
ll.insert(1, 99)
print("下标1插入99\t", ll, "长度", len(ll))
ll.reverse()
print("反转链表\t", ll, "长度", len(ll))
ll.append(666)
print("尾部追加验证tail\t", ll, "长度", len(ll))
print("查找99:", ll.search(99))
复杂度
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| prepend | O(1) | O(1) |
| append | O(1) | O(1) |
| remove | O(n) | O(1) |
| remove_all | O(n) | O(1) |
| insert | O(n) | O(1) |
| reverse | O(n) | O(1) |
| search | O(n) | O(1) |
| get_values | O(n) | O(n) |
map和join扩展
"""
map方法介绍:
语法:
map(function, *iterables)
参数:
参数1: 处理函数(内置函数/自定义/lambda)
参数2: 任何可迭代的对象(列表、元组、迭代器)
返回值:
map 迭代器(惰性求值,节省内存,只能遍历 1 次)
核心作用:
把可迭代对象里每一个元素依次传入 function 执行,批量转换数据
join方法介绍:
语法:
分割字符串.join(iterable)
调用者:
字符串(作为拼接分隔符,'--'、'->')
参数:
任意可迭代对象(列表、元组、map迭代器、生成器)
强制要求:
可迭代对象里的每一个元素必须是字符串类型,否则抛TypeError
核心作用:
用指定分隔符,把一组字符串拼成一整段字符串
"""
# ---------------------- map方法 -----------------------
# 批量转字符串
vals = [1,2,3]
str_vals = map(str, vals) # 记着返回的是迭代器
# print(next(str_vals))
# print(next(str_vals))
# print(next(str_vals))
print(list(str_vals)) # ['1', '2', '3']
# 疑问:list也能取迭代器吗
# 回答:任意迭代器(map、生成器、range、文件对象等)都能直接丢进 list(),一次性把迭代器里所有元素取出,转为普通列表。
# 自定义 lambda 处理
vals = [1,2,3]
res = map(lambda x : x**2, vals)
print(list(res)) # [1,4,9]
# 多个可迭代对象并行映射
a = [1,2]
b = [10,20]
res = map(lambda x,y: x+y, a, b)
print(list(res)) # [11,22]
# 注意点: 迭代器只能遍历一次
demo = [1,2,3]
res = map(lambda x: x**2, demo)
print(list(res)) # [1,4,9]
print(list(res)) # [] 耗尽,无元素
# ------------------------ join方法 ----------------------
# 普通列表拼接
arr = ["1","2","3"]
s = "->".join(arr)
print(s) # 1->2->3
# 配合 map 解决非字符串报错
"->".join([1,2,3]) # 报错:列表里是数字,不是字符串
"->".join(map(str, [1,2,3])) # '1->2->3' map把所有数字转字符串,再join
# 空可迭代对象返回空字符串
"->".join([]) # ""
# 常见报错原因
# 报错:int不是str
"->".join([1,2,3])
# 解决:map(str, 容器) 统一转为字符串
更多推荐
所有评论(0)