不同链表的简单总结】
1.基本单链表--首端插入和删除时间复杂度为O(1),定位操作和尾操作时间复杂度为O(n)
2.增加尾结点引用域的链接--首端、尾端插入和首端弹出元素时间复杂度为O(1),尾端删除时间复杂度为O(n)
3.循环单链表--首端和尾端插入和首端元素弹出时间复杂度为O(1)
4.双链表--两端插入和删除的时间复杂度为O(1)
链表的一些优点】
1.表结构是由一些链接起来的结点形成的,结点之间的顺序由链接关系决定,链接可以修改,因此表的结构很容易调整和修改。
2.只通过修改结点之间的链接,就能灵活的修改表的结构和数据排列方式。
3.整个表由一些小的存储块构成,比较容易安排和管理。
链表的一些缺点】
1.定位查找需要线性时间O(n),这是与线性表相比最大的劣势。
2.找当前元素的前一元素,必须从头开始扫描表结点。
3.为存储表元素,需要多用链接域。
4.简单链表的尾操作需要线性时间,增加一个尾指针,但仍不能实现高效尾端删除操作,双链表为每个结点增加第二个链接,可以实现两端的插入和删除。
# 一个简单的表结点类表结点类
class LNode:
def __init__(self,elem,next_ = None): # 只有初始化方法,给对象的两个域属性赋值
self.elem = elem
self.next = next_
# 自定义异常:
class LinkedListError(ValueError):
pass
# 单链表LList类的定义,初始化和简单操作
class LList:
def __init__(self):
self._head = None
def is_empty(self):
return self._head is None
def prepend(self,elem): # 首端插入元素需要三步
#self._head = LNode(elem,self._head)
p = LNode(elem,self._head)
self._head = p
def pop(self): # 首端删除元素,并返回该元素
if self._head is None:
raise LinkedListError("Error in LList.pop")
e = self._head.elem
self._head = self._head.next
return e
def append(self,elem): # 尾端加入元素
if self._head is None:
self._head = LNode(elem,self._head)
return
p = self._head
while p.next is not None:
p = p.next
p.next = LNode(elem)
def pop_last(self): # 删除尾端元素
if self._head is None: # 空表
raise LinkedListError("")
p = self._head
if p.next is None: # 只有一个元素 --注意p.next
e = p.elem
self._head = None
return e
while p.next.next is not None: # 直到p.next是最后结点 --注意:p.next.next
p = p.next
e = p.next.elem
p.next = None
return e
# 【两个链表链表操作】
# 链表反转】---- 单链表反转:从一个表的首端不断取下结点,将其加入另一个表的首端,就形成了一个反转的过程。
# 首端取下和加入操作都是O(1),总的时间开销为O(n)
def rev(self): # 链表元素的反转
p = None
while self._head is not None:
q = self._head # 摘下的首结点
self._head = q.next # 未摘的首结点
q.next = p # 原来摘下的结点p放在刚摘下结点的后面
p = q # 把已经反转好的部分赋值给p
self._head = p
def length(self): # 返回链表内元素的个数----具有O(n)复杂度
p, n = self._head, 0
while p is not None:
n += 1
p = p.next
return n
def find(self,pred): # 找到满足给定条件的元素
p = self._head
while p is not None:
if pred(p.elem):
return p.elem
p = p.next
def for_each(self,proc): # 以一个操作proc(比如print)为参数,作用到每一个元素上
p = self._head
while p is not None:
proc(p.elem)
p = p.next
def elements(self): # 定义生成器函数
p = self._head
while p is not None:
yield p.elem
p = p.next
def filter(self,pred): # 筛选生成器
p = self._head
while p is not None:
if pred(p.elem):
yield p.elem
p = p.next
def printall(self): # 打印链表中的元素
p = self._head
while p is not None:
print(p.elem,end='')
if p.next is not None:
print(',',end='')
p = p.next
print('')
def sort1(self): # 对单俩表进行插入排序(升序)
if self._head is None:
return
crt = self._head.next # 从首结点之后进行处理
while crt is not None:
x = crt.elem
p = self._head
while p is not crt and p.elem <= x: # 跳过小元素
p = p.next
while p is not crt: # 倒换大元素,完成元素插入工作
y = p.elem
p.elem = x
x = y
p = p.next
crt.elem = x # 回填最后元素
crt = crt.next
mlist1 = LList()
for i in range(10):
mlist1.prepend(i)
mlist1.printall()
#mlist1.rev()
mlist1.sort1()
mlist1.printall()
print("单链表内元素的个数为:",mlist1.length())
mlist1.for_each(print)
for x in mlist1.elements():
print("mlist1.elements():",x)
for i in mlist1.filter(lambda x :x%2==0):
print("能被2整除的数字",i)
# 【链表的变形和操作】
# 单链表的缺点:尾端元素操作的效率低,改进--提高后端操作的效率
# 思路:给表对象增加一个 表尾结点的引用域,对表尾进行操作的就能做到O(1)
# 含有尾结点的链表】
class LList1:
def __init__(self): # 首指针和尾指针初始化
self._head = None
self._rear = None
def prepend(self,elem): # 在首端加入元素
if self._head is None:
self._head = LNode(elem)
self._rear = self._head
else:
self._head = LNode(elem,self._head)
def append(self,elem): # 在尾端加入元素
if self._head is None:
self._head = LNode(elem)
self._rear = self._head
else:
self._rear.next = LNode(elem)
self._rear = self._rear.next
def pop(self): # 删除第一个元素,并返回
if self._head is None:
raise LinkedListError("")
e = self._head.elem
self._head = self._head.next
return e
def pop_last(self): # 删除最后一个元素 时间复杂度为O(1)
if self._head is None: # 若为空表
raise LinkedListError("")
p = self._head
if p.next is None: # 若只有一个元素
e = p.elem
self._head = None
return e
while p.next.next is not None:
p = p.next
e = p.next.elem
p.next = None
self._rear = p
return e
mlist2 = LList1()
for i in range(1,11):
mlist2.append(i)
for i in range(1,11):
print(mlist2.pop())
# 【循环单链表】
# 最后一个结点的next域不用指向None,而是指向表的第一个结点。
# 这种表对象只需一个数据域_rear,在逻辑上始终引用着表的尾结点。
class LCList: # 循环单链表
def __init__(self):
self._rear = None
def is_empty(self):
return self._rear is None
def prepend(self,elem): # 前端插入元素----时间复杂度为O(1)
p = LNode(elem)
if self._rear is None:
p.next = p # 建立一个结点的环
self._rear = p
else:
p.next = self._rear.next
self._rear.next = p
def append(self,elem): # 尾端加入元素----时间复杂度为O(1)
p = LNode(elem)
if self._rear is None:
p.next = p
self._rear = p
else:
p.next = self._rear.next
self._rear.next = p
self._rear = self._rear.next
def pop(self): # 前端弹出 ----时间复杂度为O(1)
if self._rear is None:
raise LinkedListError("")
p = self._rear.next
if self._rear is p:
self._rear = None
else:
self._rear.next = p.next
return p.elem
def printall(self): # 输出表元素----时间复杂度为O(n)
if self._rear is None:
return
p = self._rear.next
while True:
print(p.elem)
if p is self._rear:
break
p = p.next
# 【双链表】----两端插入和删除操作都能高效完成
# 从双链表的任一结点出发,可以找到其前后相邻的结点(都是O(1)操作),前一结点的引用域为prev,后一结点的引用域为next
class DLNode: # 双链表结点类
def __init__(self,elem,prev = None, next_=None):
self.elem = elem
self.prev = prev
self.next = next_
class DLList: # 双链表类
def __init__(self):
self._head = None
self._rear = None
def prepend(self,elem): # 前端插入元素
p = DLNode(elem,None,self._head)
if self._head is None:
self._rear = p
else:
p.next.prev = p
self._head = p
def append(self,elem): # 后端插入元素
p = DLNode(elem,self._rear,None)
if self._head is None:
self._head = p
else:
p.prev.next = p
self._rear = p
def pop(self): # 删除前端元素
if self._head is None:
raise LinkedListError("")
e = self._head.elem
self._head = self._head.next
if self._head is not None:
self._head.prev = None
return e
def pop_last(self): # 删除最后一个元素
if self._head is None:
raise LinkedListError("")
e = self._rear.elem
self._rear = self._rear.prev
if self._rear is None:
self._head = None
else:
self._rear.next = None
return e