背景
我们知道: 线性结构有顺序表 和 链表两种存储方式
顺序表要求: 需要连续的(内存)空间, 如果没有连续的内存空间, 则顺序表扩容就会失败
针对于这种情况, 我们可以用链表来存储, 它(链表)不需要 连续的空间。 有地儿就行。
概述:
链表属于线性结构, 它是由 节点(Node) 组成的,根据节点的不同, 以及连接方式的不同。
主要分为:
1.单向链表:节点由 元素(数值域) 和 接域(地址) 组成,后1个节点的地址为: None
2.单向循环链表:节点由 元素域(数值域) 和 链接域(地址) 组成,最后1个点的地域为:第1个节点的地址
3.双向链表:节点由 1个元素域(数值域)和 2个接域(地址域,分别指向当前节点的,后节点地址
组成, 第1个节点的(前)地址域为: None 最后1 个节点的(局)地址域为: None
4.双向循环链表节点由 1个元素域(数值域) 和 2个链接域(地址域,分别指向当前节点的前,节
点地址) 组成 第1个节点的(前)地址域为: 最后1 个节点的(后)地址最后1 个节点的(局)地址域为: 第1
个节点的地址
需求:
我们自定义代码, 模拟:(单向)链表 , 每个结点包含两个域:元素域和链接域 . 这个链接指向
链表中的下一个结点 , 而最后一个结点的链接域则指向一个空值None
①表元素域item用来存放具体的数据
②链接域next用来存放下一个结点的位置
③变量head指向链表的头结点(首结点)的位置,从head出发能找到表中的任意结点
分析:
如果采用面向对象的思路来做, 我们需要两个类
SingleNode类:表示节点类
属性: item:表示元素域,即: 当前节点存储的数据
next:表示地址域,即:当前节点的 下1个节点的 地址
SingleLinkedList类:表示 链表类
属性: head属性 表示: 头结点,即:指向链表中的第1个节点,方便我们增,删,改,查...
案例代码如下(含注释):
# 1.定义SingleNode类,表示节点类
class SingleNode:
# 初始化节点的信息
def __init__(self,item):
self.item = item # 元素域,数值域:存储区具体数据的
self.next = None # 链接域,地址域:存储下个节点的地址的
# 2.定义 SingleLinkedList类,表示:(单向)链表
class SingleLinkedList:
# 2.1初始化链表
def __init__(self,node=None):
# head指向 链表中的第一个节点
self.head = node
# 2.2 完成功能: is_empty(self) 判断链表是否为空
def is_empty(self):
return self.head == None
# 2.3 完成功能: length(self) 判断链表长度
def length(self):
# 1.定义cur(current单词)变量,记录: 当前的节点,初值为:头结点
cur = self.head
# 2.定义计数器,记录:节点个数
count = 0
# 3.循环实现,判断cur(当前节点) 是否为None,如果不是,就一直循环
while cur is not None:
# 4.进到这里,说明是1个非空节点,计数器 + 1
count += 1
# 5.判断当前节点后,cur指向下一个节点
cur = cur.next
# 6.返回结果
return count
# 2.4 完成功能: travel(self.) 遍历整个链表
def travel(self):
# 1.定义cur 变量,用于记录:链表中的每一个节点信息,初值:head(头结点)
cur = self.head
# 2.判断当前节点是否为空,不为空,就一直循环
while cur is not None:
# 3.不为空,就说明有节点信息,一直循环
print(cur.item)
# 4.cur指向下一个节点
cur = cur.next
# 2.5 完成功能: add(self, item) 链表头部添加元素
def add(self, item):
# 1.根据内容(元素值),创建节点
new_node = SingleNode(item)
# 2.设置新节点的(地址域)为self.head(之前的节点)
new_node.next = self.head
# 3.更新head属性值,因为head是指向(新的)头结点
self.head = new_node
# 2.6 完成功能: append(self, item) 链表尾部添加元素
def append(self, item):
# 1.根据内容(元素值),创建节点
new_node = SingleNode(item)
# 2.判断链表是否为空,为空,就用head直接指向新节点(充当头部节点)
if self.head == None:
self.head = new_node
else:
# 3.走这里,说明链表不为空,我们就获取链表的最后一个节点
# 3.1 创建cur变量,指向头部节点
cur = self.head
# 3.2 逐个判断,只要当前节点的地址域不为none,就一直获取
while cur.next is not None:
cur = cur.next # 更新当前节点
# 3.3 走到这里,循环结束,说明获取的节点就是最后一个节点(他的地址域为none)
# 设置他(最后一个节点)的地址域为 新节点即可
cur.next = new_node
# 2.7 完成功能: insert(self, pos, item) 指定位置添加元素
def insert(self, pos, item): # pos表示添加的位置
# 1. 判断添加位置,如果是 小于等于0的,就在首部添加。
if pos <= 0:
self.add(item)
# 2,如果添加位置,大于等于 链表的长度,就在链表尾部添加,
elif pos >= self.length():
self.append(item)
# 3。走这里,说明是在链表的中间添加,正常写逻辑即可。
else:
# 4.创建新节点
new_node = SingleNode(item)
# 5.创建cur变量,指向: 插入位置前的哪个节点,默认:头结点
cur = self.head
# 6.定义count变量,用于计数,获取插入位置前的值
count = 0
# 7.判断循环,获取插入位置前的那个元素
while count < pos - 1:
cur = cur.next
count += 1 # 计数器 + 1
# 走到这里 ,cur记录的就是:插入位置前的那个元素
# 8.设置新节点的地址域为:当前节点的地址域
new_node.next = cur.next
# 9.设置当前节点的地址域为:新的节点
cur.next = new_node
# 2.8 完成功能: remove(self, item) 删除节点
def remove(self, item):
# 1。定义变量cur,从头结点开始,逐个查找,获取到: 被删除的节点。
cur = self.head
# 2。 定义变量pre,表示: 被删除的节点的 前置节点
pre = None
# 3。只要Cur不为空,我们就一直遍历,工获取每个节点。
while cur is not None:
# 4。判断当前节点的数值域 是否和 要删除的内容(元素值) 相同
# 4.1 如果相同,说明要被删除,我们还需判断,要删除的节点是否是 头部节点.
if cur.item == item:
# 4.1.1 如果是头部节点,直接用 head 指向 cur.next即可
if cur == self.head:
self.head = cur.next
# 4.1.2 如果不是头部节点,我们用 pre变量来记录 当前节点的地址域(下个节点)
else:
pre.next = cur.next
break # return
else:
# 4.2 如果不同,说明当前节点不是要被删除的节点,我们继续判断下1个节点.
pre = cur # 当前节点(检验过)充当前置节点
cur = cur.next # 我们继续判断下一个节点
# 2.9 完成功能: search(self, item) 查找节点是否存在
def search(self, item):
# 1.定义变量cur,记录每一个节点,初值为 : 头部节点
cur = self.head
# 2.如果cur节点不为空,我们就一直遍历
while cur is not None:
# 3.判断当前节点的item,是否和我们要查找的item值一致
if cur.item == item:
# 走到这里,说明匹配上了
return True
# 4.走到这,说明没有匹配上,继续判断下一个节点
cur = cur.next
# 5.整个循环结束,走到这,说明没有找到
return False
# 3.在main中测试
if __name__ == '__main__':
# 3.1创建单个节点
sn1 = SingleNode('乔峰')
print(sn1.item) # 当前节点的 元素域值
print(sn1.next) # 当前节点的 地址域值
# 3.2创建链表
linked_list = SingleLinkedList(sn1)
print(f'目前的头节点是:{linked_list.head}')
# 3.3 判断链条是否为空
print('*'*31)
print(linked_list.is_empty())
# 3.4 打印链条的长度(元素个数)
print(f'链条长度为:{linked_list.length()}')
# 3.5 打印链表内容
linked_list.travel()
# 3.6 在链表头部添加
linked_list.add("虚竹")
linked_list.add("段誉")
# 3.7在链表尾部添加
linked_list.append("直")
linked_list.append("黄")
# 3.8 在链表中间添加(插入元素)
linked_list.insert(2,"真")
# 3.9 在链表中删除
linked_list.remove("真")
# 3.10 在链表中查找
print(linked_list.search("黄"))
# 3.15 最后,再次打印链表内容
linked_list.travel()