常见的线性表有数组与链表。链表又可以分为单链表、双向链表、环形链表。今天我们主要来进行单链表的相关操作,包括增、删、查、改、链表的反转、链表的连接等。
链表作为数据结构的一种,与数组相比,它有什么优点与不足呢?
优点
不足
节点类
我们知道链表是由一个个节点连接而成的,所以我们先创建一个节点类
# Student类(节点类) 一个Student对象就是一个节点
class Student:
def __init__(self,SchNum,name,score):
self.SchNum = SchNum
self.name = name
self.score = score
self.next = None
链表类
一个链表所需的属性有:头节点、尾节点、链表大小
# 链表类
class Link:
# 构造函数
def __init__(self):
self.head = Student(None,None,None) # 头节点为空
self.tail = self.head
self.size = 1
创建了链表我们还需要对它进行增、删、改、查等操作。如果一个链表连这些功能都无法实现的话,那么它的用处也就不大了。
增加元素
增加元素是将一个新的节点增加在链表的尾部,要增加一个节点,我们需要一下步骤:
# 添加节点
def add(self,SchNum,name,score):
stu = Student(SchNum,name,score) # 创建新节点
self.tail.next = stu # 尾节点的下一个节点为新节点
self.tail = stu # 尾节点为新节点
self.size = self.size + 1
插入节点
如果我们想要将一个节点插入到链表中,我们需要哪些步骤呢?
# 插入节点(此节点作为第index个节点)
def insert(self,SchNum,name,score,index):
if(index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
insert_stu = Student(SchNum,name,score)
for i in range(index - 1):
stu = stu.next # 推进到要插入的位置
insert_stu.next = stu.next
stu.next = insert_stu
self.size = self.size + 1
删除节点
我们假设一个链表结构 before—>delete—>after。如果我们想要删除delete节点,那么我们只需要将before的下一个节点指向after就可以了,然后链表长度-1
# 删除节点(索引为index)
def delete(self,index):
if (index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index - 1):
stu = stu.next
temp = stu.next
stu.next = temp.next
self.size = self.size - 1
改变指定位置节点的数据
# 改变指定节点的数据
def change(self,SchNum,name,score,index):
if (index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index): # 推进到要改变节点的位置
stu = stu.next
stu.SchNum =SchNum
stu.name = name
stu.score =score
返回某节点的数据
要得到某节点的数据,我们只需推进到该节点的位置,然后获取它的data属性即可。
# 返回节点的数据
def getData(self,index):
if(index > self.size): # 判断是否超过链表的长度
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index):
stu = stu.next
return [stu.SchNum,stu.name,stu.score]
获取链表的大小
这恐怕是链表中最简单的操作了,直接返回链表的size属性即可。
# 返回节点的长度
def getSize(self):
return self.size
反转链表
这个操作比较复杂一点,但是也不要害怕哟。我们知道,在链表中,当前节点知道它的下一个节点在哪,但是对它的上一个节点毫不知情。所以如果要进行链表的反转,我们需要三个指针来对链表进行操作。
原地反转
prev指向头节点
pcur是当前需要反转的节点
dummy是头节点
步骤如下:
# 反转链表(就地反转)
def jiudi_invert(self,head):
dummy = Student(None,None,None)
dummy.next = head # 创建新节点,新节点的下一个指向头(head)节点
prev = dummy.next # prev也指向头(head)节点
pcur = prev.next # pcur是当前需要反转的节点
while(pcur != None): # 循环条件:直到尾节点结束
prev.next = pcur.next # prev连接下一次需要反转的节点
pcur.next = dummy.next # 反转
dummy.next = pcur # 纠正头节点的位置(将头节点向后移动一位)
pcur = prev.next # pcur指向下一次需要反转的节点
self.head = dummy.next
新建链表,头节点插入法
pcur是要插入到新链表中的节点
prev是pcur的next
步骤如下:
# 反转链表(创建新链表,头节点插入法)
def new_invert(self,head):
dummy = self.head # 头节点当做新链表的尾节点
pcur = head.next
count = 0
while(count < self.size - 1):
prev = pcur.next # prev保存下一个要插入的节点
pcur.next = dummy # 把pcur插入到新链表中
dummy = pcur # 调整头节点的指向
pcur = prev # 下一个要插入的节点为prev
count = count + 1
self.head = dummy
连接两个链表
这个操作也相对比较简单,直接将link1的尾节点的next指向link2的头节点就可以啦。
# 连接两个链表
def connectLink(self,link):
self.tail.next = link.head.next
self.size = self.size + link.size - 1 # 链表大小相加
# Student类(节点类) 一个Student对象就是一个节点
class Student:
def __init__(self,SchNum,name,score):
self.SchNum = SchNum
self.name = name
self.score = score
self.next = None
# 链表类
class Link:
# 构造函数
def __init__(self):
self.head = Student(None,None,None) # 头节点为空
self.tail = self.head
self.size = 1
# 添加节点
def add(self,SchNum,name,score):
stu = Student(SchNum,name,score) # 创建新节点
self.tail.next = stu # 尾节点的下一个节点为新节点
self.tail = stu # 尾节点为新节点
self.size = self.size + 1
# 插入节点(此节点作为第index个节点)
def insert(self,SchNum,name,score,index):
if(index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
insert_stu = Student(SchNum,name,score)
for i in range(index - 1):
stu = stu.next # 推进到要插入的位置
insert_stu.next = stu.next
stu.next = insert_stu
self.size = self.size + 1
# 删除节点(索引为index)
def delete(self,index):
if (index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index - 1):
stu = stu.next
temp = stu.next
stu.next = temp.next
self.size = self.size - 1
# 改变指定节点的数据
def change(self,SchNum,name,score,index):
if (index > self.size):
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index): # 推进到要改变节点的位置
stu = stu.next
stu.SchNum =SchNum
stu.name = name
stu.score =score
# 返回节点的数据
def getData(self,index):
if(index > self.size): # 判断是否超过链表的长度
print('链表还没有这么长哟!请输入小一点的整数......')
else:
stu = self.head
for i in range(index):
stu = stu.next
return [stu.SchNum,stu.name,stu.score]
# 返回节点的长度
def getSize(self):
return self.size
# 反转链表(就地反转)
def jiudi_invert(self,head):
dummy = Student(None,None,None)
dummy.next = head # 创建新节点,新节点的下一个指向头(head)节点
prev = dummy.next # prev也指向头(head)节点
pcur = prev.next # pcur是当前需要反转的节点
while(pcur != None): # 循环条件:直到尾节点结束
prev.next = pcur.next # prev连接下一次需要反转的节点
pcur.next = dummy.next # 反转
dummy.next = pcur # 纠正头节点的位置(将头节点向后移动一位)
pcur = prev.next # pcur指向下一次需要反转的节点
self.head = dummy.next # 链表彻底反转完成,尾节点变为头节点
# 反转链表(创建新链表,头节点插入法)
def new_invert(self,head):
dummy = self.head # 头节点当做新链表的尾节点
pcur = head.next
count = 0
while(count < self.size - 1):
prev = pcur.next # prev保存下一个要插入的节点
pcur.next = dummy # 把pcur插入到新链表中
dummy = pcur # 调整头节点的指向
pcur = prev # 下一个要插入的节点为prev
count = count + 1
self.head = dummy
# 连接两个链表
def connectLink(self,link):
self.tail.next = link.head.next
self.size = self.size + link.size - 1 # 链表大小相加
def main():
link = Link() # 创建链表
newlink = Link() # 创建newlink,用作链表连接
for i in range(5):
newlink.add(i,'我是新链表!!!',i)
print('添加节点 => 1 , 插入节点 => 2 , 删除节点 => 3 , 改变节点数据 => 4 , 遍历链表 => 5 , 获取链表长度 => 6')
print('反转链表(就地反转) => 7 , 连接两个链表 => 8 , 反转链表(头节点插入法) => 9 , 退出 => 0')
select = -1
while select != 0:
select = int(input('请选择:'))
if(select == 1): # 添加
print('请输入相应数据')
SchNum = int(input('学号:'))
name = input('姓名:')
score = input('成绩:')
link.add(SchNum,name,score)
elif(select == 2): # 插入
print('请输入相应数据')
SchNum = int(input('学号:'))
name = input('姓名:')
score = input('成绩:')
index = int(input('要插入的位置(整数):'))
link.insert(SchNum,name,score,index)
elif(select == 3): # 删除
index = int(input('要删除节点的索引:'))
link.delete(index)
elif(select == 4): # 改变数据
print('请输入相应数据')
SchNum = int(input('学号:'))
name = input('姓名:')
score = input('成绩:')
index = int(input('要改变的位置(整数):'))
link.change(SchNum,name,score,index)
elif(select == 5): # 遍历
print('学号------姓名--------成绩')
for i in range(link.size):
data = link.getData(i)
print(str(data[0]) + ' ' + str(data[1]) + ' ' + str(data[2]))
elif(select == 6): # 获取长度
print('链表的长度为:'+str(link.getSize()))
elif(select == 7):
link.jiudi_invert(link.head)
elif(select == 8):
link.connectLink(newlink)
elif(select == 9):
link.new_invert(link.head)
if __name__ == '__main__':
main()