1. 链表
首先要明白一点:之所以做元素互换时,只有python里才可以直接用 a,b=b,a ——> 就是因为做a=10时,a可以看做是一个输入待定的函数,10并不是直接放入到了a里面,二者只是一个指向关系
注:链表与之前的顺序表对比
(因为链表只能记录头节点,故要search到其中的元素,就要从头开始遍历往后找,故访问元素的时间复杂度是O(n); 而顺序表可以直接找到目标元素,故访问元素的时间复杂度是O(1),但除了在尾部的情况外,做删除或插入时,都要把操作点位置后的所有元素依次后移,故对应的为O(n))
(1). 单链表
class Node(object):
def __init__(self,data):
self.data = data
self.next = None#指向待定
class SingleLinkList(object):#表头只有data,表尾next为None
def __init__(self,node=None):#待接节点待定
self.__head = node#头节点为私有属性(必须双下划綫),限链表内使用
def is_empty(self):
return self.__head == None#头空,则链表空
def length(self):
cur = self.__head#cur游标,用来移动遍历节点
count = 0
while cur != None:
count += 1
cur = cur.next
return count#当链表空,则头空,cur=None,不会循环,则count=0,符合
def travel(self):#遍历
cur = self.__head
while cur != None:
print(cur.data, end=' ')
cur = cur.next
print('')
def add_front(self,item):#链首加元素,头不变
node = Node(item)
'链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
node.next = 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=self.head=None ,没cur.next,故需另做判断
cur = cur.next
cur.next = node
def insert(self,pos,item):#指定位置加元素
if pos <= 0:
self.add_front(item)#链首
elif pos > (self.length()-1):
self.append(item)#链尾
else:
pre = self.__head#游标从头节点起步
count = 0
"""
此处有个难点:比如pos=2,即要插入到第3个位置(不算头结点),由于插入位置的前个节点的指向也需更改,
故条件取到count < (pos-1) 而不是count < pos
"""
while count < (pos-1):
count += 1
pre = pre.next
#退出循环时,pre指向pos-1位置
node = Node(item)
node.next = pre.next#pre.next是被新节点占去位置的原节点,将其放到新节点的后面
pre.next = node
def remove(self,item):#删除节点
'要考虑链首、尾、为空'
cur = self.__head
pre = None #游标cur的前个节点
while cur != None:
if cur.data == item:
'先判断该节点是否为头结点'
if cur == self.__head:
self.__head = cur.next#当只有头结点,将其删除后,self.__head就该是None,而cur.next就是None
else:
"""
注意:pre本身原是无节点意义的,是因为在判断条件下会先经历
pre = cur cur = cur.next 的位置后移,所以pre也有了和cur一样的节点属性!
"""
pre.next = cur.next#把待删除节点的下个节点指给待删除节点的前个节点
break#必须有!
else:
pre = cur
cur = cur.next#pre与cur同时后移
def search(self,item):#查找某节点是否存在
cur = self.__head
while cur != None:
if cur.data == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8)#8 1 2 3 4
ll.insert(-1, 9)#9 8 1 2 3 4
ll.travel()
ll.insert(2, 100)#9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200)#9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9)#删头
ll.travel()#8 100 1 2 3 4 200
ll.remove(200)#删尾
ll.travel()#8 100 1 2 3 4
ll.remove(1)
ll.travel()#8 100 2 3 4
(2). 单向循环链表
class Node(object):
def __init__(self, data):
self.data = data
self.next = None # 指向待定
'单向循环列表'
class SingleCycleLinkList(object):
def __init__(self, node=None): # 待接节点待定
self.__head = node # 头节点为私有属性(必须双下划綫),限链表内使用
if node:
node.next = node # 若链表在初始时就已经传入了节点,则next指向自己
def is_empty(self):
return self.__head == None # 头空,则链表空
def length(self):
if self.is_empty():
return 0 # 函数遇return则执行结束
cur = self.__head # 游标
count = 1 # 若非空,则至少有头节点,计数从1起
while cur.next != self.__head: # 至尾结束
count += 1
cur = cur.next
return count
def travel(self): # 遍历
if self.is_empty(): # 当空,cur = self.__head=None,但None不具备next属性,故空链要单独列出
return
cur = self.__head
while cur.next != self.__head:
print(cur.data, end=' ')
cur = cur.next
print(cur.data) # 尾节点在循环退出时没打出,故补上
def add_front(self, item): # 链首加元素,头不变
node = Node(item)
'链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
if self.is_empty():
self.__head = node
node.next = node
return
cur = self.__head
while cur.next != self.__head:
cur = cur.next
# 退出循环,cur此时为尾节点
node.next = self.__head # node接到链首,此时self.__head更新后指向的是node了
self.__head = node # 头结点接node
cur.next = self.__head
def append(self, item): # 链表尾加元素
node = Node(item) # 待加节点
if self.is_empty():
self.__head = node
node.next = node
else:
cur = self.__head
while cur.next != self.__head:
cur = cur.next
node.next = self.__head
cur.next = node
def insert(self, pos, item): # 指定位置加元素
if pos <= 0:
self.add_front(item) # 链首
elif pos > (self.length() - 1):
self.append(item) # 链尾
else:
pre = self.__head # 游标从头节点起步
count = 0
"""
此处有个难点:比如pos=2,即要插入到第3个位置(不算头结点),由于插入位置的前个节点的指向也需更改,
故条件取到count < (pos-1) 而不是count < pos
"""
while count < (pos - 1):
count += 1
pre = pre.next
# 退出循环时,pre指向pos-1位置
node = Node(item)
node.next = pre.next # pre.next是被新节点占去位置的原节点,将其放到新节点的后面
pre.next = node
def remove(self, item): # 删除节点
'要考虑链首、尾、为空'
if self.is_empty():
return
cur = self.__head
pre = None # 游标cur的前个节点
while cur.next != self.__head:
if cur.data == item:
'先判断该节点是否为头结点'
if cur == self.__head: # =是指向,==不是
# 当待删为头结点,则先找尾结点
rear = self.__head
while rear.next != self.__head:
rear = rear.next # 后移, 直至找到尾节点
self.__head = cur.next # cur此时就是头结点,把头指向链首cur.next,即链首成了新的头结点了
rear.next = self.__head # 尾指向新的头
else: # 中间节点
"""
注意:pre本身原是无节点意义的,是因为在判断条件下会先经历
pre = cur cur = cur.next 的位置后移,所以pre也有了和cur一样的节点属性!
"""
pre.next = cur.next # 把待删除节点的下个节点指给待删除节点的前个节点
'此处不能像之前用break!因为break只是退出了while循环,而此处需要函数结束'
return
else:
pre = cur
cur = cur.next # pre与cur同时后移
# 退出循环,cur此时为尾节点
if cur.data == item:
if cur == self.__head: # 只有头结点
self.__head = None
else:
pre.next = cur.next # cur的前节点直接指向cur的后节点
def search(self, item): # 查找某节点是否存在
if self.is_empty():
return False
cur = self.__head
while cur.next != self.__head:
if cur.data == item:
return True
else:
cur = cur.next
# 退出循环,cur此时为尾节点
if cur.data == item:
return True
return False
if __name__ == '__main__':
ll = SingleCycleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8) # 8 1 2 3 4
ll.insert(-1, 9) # 9 8 1 2 3 4
ll.travel()
ll.insert(2, 100) # 9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200) # 9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9) # 删头
ll.travel() # 8 100 1 2 3 4 200
ll.remove(200) # 删尾
ll.travel() # 8 100 1 2 3 4
ll.remove(1)
ll.travel() # 8 100 2 3 4
(3). 双向链表
class Node(object):
def __init__(self,data):
self.data = data
self.prev = None#前驱节点
self.next = None#后置节点
'双向链表'
class DoubleLinkList(object):
def __init__(self,node=None):#待接节点待定
self.__head = node#头节点为私有属性(必须双下划綫),限链表内使用
def is_empty(self):
return self.__head == None#头空,则链表空
def length(self):
cur = self.__head#cur游标,用来移动遍历节点
count = 0
while cur != None:
count += 1
cur = cur.next
return count#当链表空,则头空,cur=None,不会循环,则count=0,符合
def travel(self):#遍历
cur = self.__head
while cur != None:
print(cur.data, end=' ')
cur = cur.next
print('')
"""
由于双向链表的以上前4个属性及search()与单链表一样,所以可以直接用面向对象的继承方法,写作如下:
class DoubleLinkList(SingleLinkList)
则以上5个属性就无需再写了,因为直接继承过来了
"""
def add_front(self,item):#链首加元素,头不变
node = Node(item)
'链首本身没有next,等号代表指向,self.__head本身指向的是原head后的第一个节点,现在要把该节点接到新的节点的后面'
node.next = self.__head
self.__head = node
node.next.prev = node#比单链多一个node的下个节点对node的指向
def append(self,item):#链表尾加元素
node = Node(item)#待加节点
if self.is_empty():
self.__head=node
else:
cur = self.__head
while cur.next != None:#找到表尾;当表为空,cur=self.head=None ,没cur.next,故需另做判断
cur = cur.next
cur.next = node
node.prev = cur#比单链多一个cur的下个节点node对cur的指向
def insert(self,pos,item):#指定位置加元素
if pos <= 0:
self.add_front(item)#链首
elif pos > (self.length()-1):
self.append(item)#链尾
else:#此处与单链不同
cur = self.__head#游标从头节点起步
count = 0
while count < pos:
count += 1
cur = cur.next
#退出循环时,cur指向pos位置
node = Node(item)
"""
以下四字因操作先后顺序不同,也可改成如下:
node.next = cur
node.prev = cur.prev
cur.prev = node
node.prev.next = node
"""
node.next = cur
node.prev = cur.prev
cur.prev.next = node
cur.prev = node
def remove(self,item):#删除节点
'要考虑链首、尾、为空'
cur = self.__head
while cur != None:
if cur.data == item:
'先判断该节点是否为头结点'
if cur == self.__head:
self.__head = cur.next#当只有头结点,将其删除后,self.__head就该是None,而cur.next就是None
if cur.next:#当只有cur这一个节点的时候,cur.next为None,是没有prev的
cur.next.prev = None
else:
cur.prev.next = cur.next#把待删除节点的下个节点指给待删除节点的前个节点
if cur.next:
cur.next.prev = cur.prev
break#必须有!
else:
cur = cur.next#pre与cur同时后移
def search(self,item):#查找某节点是否存在
cur = self.__head
while cur != None:
if cur.data == item:
return True
else:
cur = cur.next
return False
if __name__ == '__main__':
ll = DoubleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.append(3)
ll.append(4)
ll.add_front(8)#8 1 2 3 4
ll.insert(-1, 9)#9 8 1 2 3 4
ll.travel()
ll.insert(2, 100)#9 8 100 1 2 3 4
ll.travel()
ll.insert(10, 200)#9 8 100 1 2 3 4 200
ll.travel()
ll.remove(9)#删头
ll.travel()#8 100 1 2 3 4 200
ll.remove(200)#删尾
ll.travel()#8 100 1 2 3 4
ll.remove(1)
ll.travel()#8 100 2 3 4