初步使用链表
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 功能:
# 创建空表 删除表 判断表是否为空 判断表是否满
# 首插 尾插 一般情况插入
# 首删 尾删 一般情况和删
# 扫描、查找和遍历 1、按元素查找2、按下标查找
class LNode: # 表节点类
def __init__(self, elem, next_ = None):
self.elem = elem
self.next = next_
# 创建空链表
# head = LNode(None)
# 创建表头(创建链表)
head = LNode(0) # head不变,需要使用的时候用一个中间变量(指针或游标)引用他
# 创建一些节点
pre = head
for i in range(1,10):
# node = LNode(i)
# pre.next = node
pre.next = LNode(i)
pre = pre.next
# if i == 9:
# node.next = None
# 按下标定位,首节点下标为0
p = head
i = 5
while p is not None and i>0:
i = i-1 # 当i=1时执行最后一次循环,最后i=0
p = p.next
print('下标为5的元素:',p.elem)
p = head
for i in range(5): # 打印下标为5的元素(第6个元素)
if p is not None:
p = p.next
print('下标为5元素:',p.elem)
# 按元素定位
p = head
while p is not None and (p.elem != 5): # 打印节点元素为5的元素
p = p.next
else:
print('查询节点元素为5的元素:',p.elem)
# 头插:插在表头的前面(原来的head前面)
q = LNode(100)
q.next = head
head = q
# 遍历:打印所有节点
print('遍历:以下打印链表所有节点:')
p = head
while p is not None: # 节点存在就打印出节点元素
print(p.elem)
p = p.next
# 求表长度: 表头+表节点+表尾
def length(head):
p = head; cnt = 0 #计数习惯初始为0
while p is not None: # p is None表示这个节点不存在,判断尾节点:p.next is None
cnt = cnt + 1 # p is not None就加1
p = p.next
return cnt
length = length(head)
print('链表长度为:',length)
# 头插,头插是插在head前面
# q = LNode(0)
# q.next = head
# head = q
# 中间插入:让游标pre指向待删除节点的前一节点、 即使pre指向的是尾节点可可以
# q = LNode(13)
# q.next = pre.next
# pre.next = q # 注意: 该语句不能和上一条语句顺序互换,如果先执行该条语句,链表插入位置的后面部分会被Python回收机制回收
# 尾插, 要先遍历一遍链表找到尾节点
# node = LNode(29)
# pre = head
# while pre.next is not None:
# pre = pre.next # 游标向后移动一位
# else:
# pre.next = node
# node.next = None
#头删
# head = head.next
#一般删: pre指向需要删除节点的前一节点
# pre.next = pre.next.next
# 查找、遍历:对表中一切元素的检查都要从表头变量head开始
# p = head
# while p is not None ''' and (其他条件)''':
# '''对p所指节点的操作'''
# p = p.next #游标p向后移动一位
# 链表复杂度分析:
单链表
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
class LinkListUnderflow(ValueError): # 自定义异常类
pass
class LNode: # 表节点类(重要概念) -->该类的一个对象表示一个节点
def __init__(self, elem, next_ = None):
self.elem = elem
self.next = next_
# 初始化一个链表玩一下
# list1 = LNode(1)
# p = list1
# for i in range(2,11):
# p.next = LNode(i) # 一边循环,一边新增节点,同时让指针指向新增节点
# p = p.next
# 打印每个元素出来看一下
# p = list1
# while p is not None:
# print(p.elem)
# p = p.next
###################
''' 单链表 '''
###################
# 节点类和单链表对象类的区别:
# 节点类:一个属性表示一个数据域或者指针域
# 单链表对象类:一个属性表示一个对象,这个对象是节点类实例化的对象,即一个节点
# 节点类实例化后的对象表示 --> 节点
# 单链表对象类实例化后的对象表示 --> 链表--汇集对象
class LList: # 单链表对象类(重要概念) -->该类的属性是表节点类的对象
def __init__(self):
self._head = None # 这个是表头,表头代表链表,表头地址即表示链表地址,和数组一样:数组的地址就是第一个元素的地址
def is_empty(self):
return self._head is None
# 头插
def prepend(self, elem):
# p = LNode(elem) # 创建新的节点对象p
# p.next = self._head
# self._head = p # 注释的这三行等价于下面一行
self._head = LNode(elem,self._head) # ???使用LNode类
#追加到表尾
def append(self, elem):
# 空表:一个节点都不存在,包括表头、中间节点、尾节点
if self._head is None: # 如果是空表直接插在头结点后面,结束
self._head = LNode(elem)
return
p = self._head
while p.next is not None:
p = p.next
p.next = LNode(elem) # 表尾节点指向新创建的节点
# 插入中间节点
def insert(self, index, elem):
if (self._head is None) and (self._head.next is None):
raise LinkListUnderflow("in insert")
p = self._head
while p is not None and index>1:
index = index-1
p = p.next
node = LNode(elem)
node.next = p.next # 将新增节点先连上链表
p.next = node # 断开前一节点,将前一节点指向新增节点
###注意:一定要先连再断,如果先断开前一节点,Python回收机制会将该节点后的的部分回收,相当于删除了链表后面部分
# 弹出表头元素
def pop(self):
if self._head is None:
raise LinkListUnderflow('in pop')
e = self._head.elem
self._head = self._head.next
return e
# 弹出表尾元素
def pop_last(self):
if self._head is None: # 空表,一个节点也没有,在内存中为空
raise LinkListUnderflow('in pop_last')
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
return e
# 删除表中第一个元素为elem的节点
def remove_any(self, elem): #两个游标,cur用来寻找elem,pre始终指向cur的前一个节点,待执行删除的时候用pre指向cur的后一节点
pre = None
cur = self._head
while cur is not None:
if cur.elem == elem:
if pre is None: # cur是头结点
self._head = cur.next # head指向原头结点的下一个节点
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
else:
raise LinkListUnderflow('in remove_any')
# 删除所有元素为elem的节点
def remove_elem_all(self,elem):
pre = None
cur = self._head
while cur is not None:
if cur.elem == elem:
if pre is None:
self._head = cur.next
else:
pre.next = cur.next
cur = pre.next # 将cur指向删除节点的下一个节点
else:
pre = cur
cur = cur.next
def remove_any2(self, elem): # 一定要有一个游标指向待删除节点的前一个节点
if self._head == None:
raise LinkListUnderflow('in remove_any2')
if self._head.elem == elem:
self._head = self._head.next
return
p = self._head
while p is not None and (p.next.elem != elem):
p = p.next
else:
p.next = p.next.next
def remove_index(self,index):
pre = self._head
while pre is not None and index>1: # 让p指向待删除节点的前一节点
index = index-1
pre = pre.next
pre.next = pre.next.next
# 按照元素查询
def find(slef, elem):
p = self._head
while p is not None:
if p.elem == elem:
return p.elem
p = p.next
# 按照下标查询
def find_index(self, index):
p = self._head
while p is not None and i>0:
i = i-1
p = p.next
return p.elem
def find_first(self):
pass # 略
def find_last(self):
pass # 略
# 遍历输出所有元素
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('')
# 遍历执行某种操作proc
# proc的实参是可以作用于表元素的操作函数,它将作用于表的每一个元素,这里以打印为例
# proc常常可以用一个lambda表达式来定制
def for_each(self, proc):
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
# 筛选生成器,相比于定义迭代器,它加上了一个筛选的函数pred,我们把这个pred
def find_filter(self,pred):
p = self._head
while p is not None:
if pred(p.elem):
yield p.elem
p = p.next
# 用于测试pred的方法 -->输出偶数
def is_oddnumber(self,em):
if em % 2 == 0:
return True
else:
return False
# 反转链表
def reverse(self):
pre = None
cur = self._head
while cur is not None:
q = cur
cur = q.next # 游标cur向后移动
q.next = pre # 每次更改p节点的指向
pre = q # 游标pre向后移动
self._head = pre
# 这里也可以不引入cur,全部用self._head替换,表示表头不断向后移动
# mylist = LList()
# for i in range(10):
# mylist.prepend(i)
# for i in range(11,20):
# mylist.append(i)
# mylist.prepend(10)
# mylist.insert(3,300)
# mylist.insert(4,300)
# mylist.insert(7,300)
# mylist.insert(8,400)
# mylist.insert(9,400)
# mylist.insert(11,400)
# mylist.remove_any(300)
# mylist.remove_elem_all(400)
# mylist.remove_index(1)
# mylist.printall()
# mylist.reverse()
# mylist.printall()
# mylist.for_each(print) #以打印为例
# for x in mylist.elements():
# print(x)
# for x in mylist.find_filter(mylist.is_oddnumber):
# print(x)
单链表加上尾节点引用域
# 增加尾节点引用域
# 除了操作表头和表尾不一样,其他方法都一样可以使用
# 类设计要遵循内在的一致性
class LList1(LList):
def __init__(self):
LList.__init__(self) # 继承父类LList的初始化函数
self._rear = None # 尾节点引用域
# self._head = None # head就是指向链表对象的引用,链表的头结点所在的地址表示链表的地址,相当于数组
def prepend(self, elem):
if self._head is None:
self._head = LNode(elem, self._head)
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._head)
self._rear = self._head # 这里_head的指针域是自己,这就等于_rear指向了尾节点
else:
self._rear.next = LNode(elem)
self._rear = self._rear.next
def pop_last(self):
if self._head is None:
raise LinkListUnderflow('in pop_last')
p = self._head
if self._head.next is None:
e = p.elem
self._head = None
return e
while p.next.next is not None: # 把p.next看成一个节点在移动、或者判断循环终止的时候p是指哪一个节点
p = p.next
e = p.next.elem # 此时p.next是指尾节点
p.next = None
self._rear.next = p
return e
# mylist2 = LList1()
# mylist2.prepend(99)
# for i in range(1,10):
# mylist2.append(i)
# for x in mylist2.find_filter(lambda y: y%2 == 1):
# print(x)
# mylist2.pop_last()
循环单链表
# 单链表加上尾节点引用域:表尾查找和追加O(1),删除O(n)。因为删除需要通过遍历找到尾节点的前一个节点
# 循环单链表:表尾查找和追加O(1),删除O(n)
# 链表对象用来记录表尾节点更合适
# 循环单链表与普通单链表的差异:1、循环遍历的结束控制 2、没有表头引用域,所以单链表涉及表头的方法这里不能继承使用
class LCList: # 循环单链表类
def __init__(self):
self._rear = None #尾节点引用域
def is_empty(self):
return self._rear is None
def prepend(self, elem):
# p = LNode(elem)
if self._rear is None:
# p.next = p # 建立一个节点的环
# self.-repr = p
self._rear = LNode(elem)
self._rear.next = self._rear
else:
# p.next = self._rear.next
# self._rear.next = p
self._rear.next = LNode(elem, self._rear.next)
def append(self, elem):
self.prepend(elem)
self._rear = self._rear.next # 表尾改为追加的节点
def pop(self):
if self._rear is None:
raise LinkListUnderflow("in pop")
p = self._rear.next
if self._rear is p:
self._rear = None
else:
self._rear.next = p.next
return p.elem
def printall(self):
if self.is_empty():
return
p = self._rear.next
while True:
print(p.elem, end=', ')
if p is self._rear:
break
p = p.next
# mylist3 = LCList()
# for i in range(1,10):
# mylist3.prepend(i)
# for i in range(11,20):
# mylist3.append(i)
# for x in mylist3.find_filter(lambda y: y%2 == 1):
# print(x)
# mylist3.pop()
# mylist3.printall()