(一)链表概念:
线性结构有顺序表和链表两种存储方式。链表( Linked list )是一种常见的线性结构,也是基础数据结构,不会按线性的顺序存储数据,而是在每⼀个节点里存到下⼀个节点的指针 (Pointer)。 所以链表结构不需要一块连续的内存空间 ,它通过 ” 指针 ” 将一组零散的内存块串联起来使用。由节点(Node)组成,根据节点以及连接方式的不同,分为单向链表、单向循环链表、双向链表、双向循环链表。本文用python模拟单项链表展示其工作原理。
(二) 相关类:
SingleNode: 表示节点类
SingleLinkedList:表示(单向)链表类
(三)相关方法:
is empty():判断链表是否为空
length():查看链表长度
travel():遍历整个链表
add():链表头部添加元素
append():链表尾部添加元素
insert():中间指定位置添加元素
remove():删除节点
search():查找节点是否存在
(四)实现步骤:
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(默认)指向 链表中的第1个节点.
self.head = node
2:实现 is empty()、length()、travel()、add()、append() 相关方法
# 2.2 完成功能: is_empty(), 判断列表是否为空
def is_empty(self):
# 思路: 判断头结点是否为空.
# 写法1: if.else
# if self.head == None:
# return True
# else:
# return False
# 写法2: 三目运算符
# return True if self.head == None else False
# 写法3: 最终版
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指向下1个节点.
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指向下1个节点.
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. 走这里, 说明链表不为空, 我们就获取链表的最后1个节点.
# 3.1 创建cur变量, 默认指向头部节点.
cur = self.head
# 3.2 逐个判断, 只要当前节点的地址域不为None, 就一直获取.
while cur.next is not None:
# 更新当前节点
cur = cur.next
# 3.3 走到这里, 循环结束, 说明获取的节点就是最后1个节点(它的地址域为None)
# 设置它(最后1个节点)的地址域为: 新的节点即可.
cur.next = new_node
3:实现insert()指定位置添加元素功能
图解:
# 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
# 计数器 + 1
count += 1
# 走到这里, cur记录的就是: 插入位置前的那个元素.
# 8. 设置新节点的地址域为: 当前节点的地址域
new_node.next = cur.next
# 9. 设置当前节点的地址域为: 新的节点.
cur.next = new_node
4:实现remove()删除节点功能
1)图解删除的是头部节点
2)图解删除的是中间节点
# 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 # 我们继续判断下1个节点.
5:完成search()查询功能
# 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. 走这里, 说明没有匹配上, 我们继续判断下一个节点.
else:
cur = cur.next
# 5. 整个循环结束, 走到这里, 说明没有找到.
return False
6:main方法中测试
# 3. 在main方法中测试
if __name__ == '__main__':
# 3.1 创建单个节点.
sn1 = SingleNode('孙悟空')
print(sn1.item) # 当前节点的: 元素域值
print(sn1.next) # 当前节点的: 地址域值
print('-'*30)
# 3.2 创建链表.
linked_list = SingleLinkedList(sn1)
print(f'目前的头节点是:{linked_list.head}')
# 3.3 判断链表是否为空.
print(linked_list.is_empty())
# 3.4 打印链表的长度(元素个数)
print(f'链表的长度为:{linked_list.length()}')
print('-' * 30)
# 3.6 演示添加, 在: 链表头部添加.
# linked_list.add('虚竹')
# linked_list.add('段誉')
# 3.7 演示添加, 在: 链表尾部添加.
linked_list.append('唐僧')
linked_list.append('猪八戒')
linked_list.travel()
print(f'链表的长度为:{linked_list.length()}')
print('-' * 30)
# 3.8 演示添加, 在: 链表中间添加(插入元素).
# 本来是: 乔峰, 虚竹, 段誉, 添加后是: 乔峰, 虚竹, 夯哥, 段誉
linked_list.insert(2,'沙和尚')
# 3.9 演示 删除.
# 3.10 最后, 再次打印链表内容.
linked_list.travel()
print('-' * 30)
# 3.11 演示 查找.
print(linked_list.search('唐僧'))
print(linked_list.search('白骨精'))