目录
一、链表的定义及其特点
链表是一种在存储单元上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过链表中的指针链接次序实现。链表是由一系列的结点组成,结点可以在运行时动态生成。每个结点包含两部分:数据域与指针域。数据域存储数据元素,指针域存储下一结点的指针。
二、单向链表
单向链表也叫单链表,它的每个结点包含两个域,一个数据域和一个指针域。这个指针指向链表中的下一个结点,最后一个结点的指针域指向一个空值(None)。
1、定义结点(Node)类
结点的数据结构为数据(data)与 指针(next)
class Node:
"""一个链表结点"""
def __init__(self, node_data=None, next=None):
self._data = node_data # 数据域
self._next = next # 指针域
def get_data(self):
"""获取结点数据域"""
return self._data
def set_data(self, node_data):
"""设置结点数据域"""
self._data = node_data
data = property(get_data, set_data)
def get_next(self):
"""获取结点指针域"""
return self._next
def set_next(self, node_next):
"""设置结点指针域"""
self._next = node_next
next = property(get_next, set_next)
2、定义链表(Linklist)类
(1)在单链表中第一个结点为头结点(即头指针指向的结点为单链表第一个结点,后续简称头结点),从头结点出发可以遍历整个链表,进行元素查找,插入和删除,非常重要。一般不移动头指针(head)。
(2)单链表中最后一个节点为尾节点,其指针为None,表示结束。
class Linklist:
def __init__(self):
"""创建头结点"""
self.head = Node()
三、单链表基本操作的实现
1、增
(1)append方法
作用:
在链表的尾部添加结点
算法思路:
第一步:看判断当前链表是否为空。
第二步:如果为空将新结点插入到头结点后,如果不为空则创建一个外部引用指向最后一个结点,再将新节点插入到链表尾部。
代码实现:
def append(self, value):
"""后插法插入新结点"""
node = Node(value) # 创建新插入的结点对象,数据域为value, 指针域为默认的None
if self.head.next is None: # 链表为空
self.head.next = node # 将新结点插入到头结点后
else:
curnode = self.head.next # 生成一个外部引用指向首元结点
while curnode.next is not None: # 使curnode指向最后一个结点
curnode = curnode.next # curnode指向下一个结点
curnode.next = node # 将新节点插入到链表尾部
(2) insert方法
作用:
在带头结点的单链表中第index个位置插入值为value的新结点
算法思路:
第一步:判断要插入的位置是否合法(不能<=0,和超过链表长度+1)
第二步:如果不合法则返回False(插入失败)
第三步:如果合法,则使插入结点指向原链表中第index个结点,原链表中的第index-1个结点指向插入结点,达到插入结点的效果
代码实现:
def insert(self, index, value):
"""在带头结点的单链表中第index个位置插入值为value的新结点"""
p = Node(value) # 生成新结点p
curnode = self.head # 初始化,curnode指向头结点
count = 0
while (curnode is not None) and (count < index-1): # 顺链域向后查找,直到curnode为空或者curnode指向第index-1个结点
curnode = curnode.next
count += 1
if (curnode is None) or (count > index): # index > 表长+1 或者 index < 1
return False
p.next = curnode.next # 将新结点p的指针域指向原第index个结点
curnode.next = p # 将第index-1个结点的指针域指向新结点
2、删
(1)remove方法
作用:
删除链表中第一个数据域为value(传入的参数)的结点
算法思路:
第一步:初始化,创建两个外部引用,一个指向要删除的结点,一个指向删除结点的前一结点
第二步:判断要删除的数据是否存在于单链表结点的数据域中,如果不存在返回False(删除失败),并抛出错误(要删除的数据不在链表中)
第三步:如果存在,则使要删除结点的前一结点指向要删除的结点的后一结点
代码实现:
def remove(self, value):
"""删除链表中第一个数据域为value的结点"""
curnode = self.head # 初始化,curnode指向头结点
pre = None # 创建一个外部引用用于引用要删除结点的前一结点
while curnode is not None: # 顺链域向后查找,直到curnode为空
if curnode.data == value: # 如果找到了就跳出循环
break
pre = curnode
curnode = curnode.next
if curnode is None: # 链表中没有数值域为value的结点
raise ValueError(f"{value} is not in list")
# 链表中有数值域为value的结点
if pre is None: # 要删除的结点为首元结点
self.head.next = curnode.next # 将头结点的指针域指向原链表第二个结点
else:
pre.next = curnode.next # 将删除结点的前一个结点的指针域,指向删除结点的后一个结点
3、查
(1)is_empty方法
作用:
判断链表是否为空
算法思路:
判断头结点是否为空,为空则是空链表
代码实现:
def is_empty(self):
"""判断链表是否为空"""
return self.head.next == None # 判断头结点是否为空,为空则是空链表
(2)get_data方法
作用:
在带头结点的单链表中根据传入的序号index获取该结点的数值域
算法思路:
创建一个外部引用,如果输入的index值合法(index值不超过链表长度并且不小于1),则将外部引用指向第index个结点,获取该结点的数据域,否则返回False(获取失败)
代码实现:
def get_data(self, index):
"""在带头结点的单链表中根据序号index获取结点的数值域"""
curnode = self.head.next # 初始化,curnode指向首元结点
count = 1 # 计数器count初值赋为1
while (curnode is not None) and (count < index): # 顺链域向后查找,直到curnode为空或者curnode指向第index个结点
curnode = curnode.next
count += 1
if (curnode is None) or (index < count):
# 如果curnode is None则说明需要查找的index值超过了链表长度
# 如果index < count则说明index < 1
return False
# 如果输入的索引值合法
r_data = curnode.data # 取第index个元素的数据域
return r_data
(3)size方法
作用:
查看链表的长度(不包括头结点)
算法思路:
第一步:初始化,创建一个外部引用指向首元结点,一个计数器并赋值0
第二步:顺链域向后查找,直到t为空
第三步:返回链表长度
代码实现:
def size(self):
"""查看链表的长度(不包括头结点)"""
t = self.head.next # 创建t指向首元结点
count = 0 # 初始化计数器赋值0
while t is not None: # 顺链域向后查找,直到t为空
count += 1 # 链表长度+1
t = t.next # t指向下一结点
return count # 返回链表长度
(4)search方法
作用:
查询数据是否存在于链表中
算法思路:
第一步:生成一个外部引用指向首元结点,若首元结点为空,则直接返回False(查询的参数不存在于链表中)
第二步:若首元结点不为空,则顺链域往下搜索,并判断结点的数值域是否为要搜索的参数,直到curnode为空
代码实现:
def search(self, value):
"""查询传入的参数是否存在于链表中"""
curnode = self.head.next # 生成一个外部引用指向首元结点
while curnode is not None: # 顺链域往下搜索,直到curnode为空
if curnode.data == value:
return True # 搜索的数据在链表中
curnode = curnode.next
return False # 搜索的数据不在链表中
4、遍历
(1)printf方法
作用:
打印链表中所有结点的数据域
代码实现:
def printf(self):
"""打印链表中所有结点的数据域"""
if self.size() == 0:
print("链表中没有元素")
else:
curnode = self.head.next # 初始化,curnode指向首元结点
while curnode is not None: # 顺链域向后查找,直到curnode为空
print(curnode.data, end=" ") # 打印结点的数据域
curnode = curnode.next # 指向下一个结点
print() # 打印一个空行
四、完整代码(包含测试代码)
# Node类
class Node:
"""一个链表结点"""
def __init__(self, node_data=None, next=None):
self._data = node_data # 数据域
self._next = next # 指针域
def get_data(self):
"""获取结点数据域"""
return self._data
def set_data(self, node_data):
"""设置结点数据域"""
self._data = node_data
data = property(get_data, set_data)
def get_next(self):
"""获取结点指针域"""
return self._next
def set_next(self, node_next):
"""设置结点指针域"""
self._next = node_next
next = property(get_next, set_next)
def __str__(self):
"""字符串"""
return str(self._data)
# Linklist类
class Linklist:
def __init__(self):
"""创建头结点"""
self.head = Node()
def is_empty(self):
"""判断链表是否为空"""
return self.head.next == None # 判断头结点是否为空,为空则是空链表
def append(self, value):
"""后插法插入新结点"""
node = Node(value) # 创建新插入的结点对象,数据域为value, 指针域为默认的None
if self.head.next is None: # 链表为空
self.head.next = node # 将新结点插入到头结点后
else:
curnode = self.head.next # 生成一个外部引用指向首元结点
while curnode.next is not None: # 使curnode指向最后一个结点
curnode = curnode.next # curnode指向下一个结点
curnode.next = node # 将新节点插入到链表尾部
def get_data(self, index):
"""在带头结点的单链表中根据序号index获取结点的数值域"""
curnode = self.head.next # 初始化,curnode指向首元结点
count = 1 # 计数器count初值赋为1
while (curnode is not None) and (count < index): # 顺链域向后查找,直到curnode为空或者curnode指向第index个结点
curnode = curnode.next
count += 1
if (curnode is None) or (index < count):
# 如果curnode is None则说明需要查找的index值超过了链表长度
# 如果index < count则说明index < 1
return False
# 如果输入的索引值合法
r_data = curnode.data # 取第index个元素的数据域
return r_data
def size(self):
"""查看链表的长度(不包括头结点)"""
t = self.head.next # 创建t指向首元结点
count = 0 # 初始化计数器赋值0
while t is not None: # 顺链域向后查找,直到t为空
count += 1 # 链表长度+1
t = t.next # t指向下一结点
return count # 返回链表长度
def remove(self, value):
"""删除链表中第一个数据域为value的结点"""
curnode = self.head # 初始化,curnode指向头结点
pre = None # 创建一个外部引用用于引用要删除结点的前一结点
while curnode is not None: # 顺链域向后查找,直到curnode为空
if curnode.data == value: # 如果找到了就跳出循环
break
pre = curnode
curnode = curnode.next
if curnode is None: # 链表中没有数值域为value的结点
raise ValueError(f"{value} is not in list")
# 链表中有数值域为value的结点
if pre is None: # 要删除的结点为首元结点
self.head.next = curnode.next # 将头结点的指针域指向原链表第二个结点
else:
pre.next = curnode.next # 将删除结点的前一个结点的指针域,指向删除结点的后一个结点
def insert(self, index, value):
"""在带头结点的单链表中第index个位置插入值为value的新结点"""
p = Node(value) # 生成新结点p
curnode = self.head # 初始化,curnode指向头结点
count = 0
while (curnode is not None) and (count < index-1): # 顺链域向后查找,直到curnode为空或者curnode指向第index-1个结点
curnode = curnode.next
count += 1
if (curnode is None) or (count > index): # index > 表长+1 或者 index < 1
return False
p.next = curnode.next # 将新结点p的指针域指向原第index个结点
curnode.next = p # 将第index-1个结点的指针域指向新结点
def printf(self):
"""打印链表中所有结点的数据域"""
if self.size() == 0:
print("链表中没有元素")
else:
curnode = self.head.next # 初始化,curnode指向首元结点
while curnode is not None: # 顺链域向后查找,直到curnode为空
print(curnode.data, end=" ") # 打印结点的数据域
curnode = curnode.next # 指向下一个结点
print() # 打印一个空行
def search(self, value):
"""查询传入的参数是否存在于链表中"""
curnode = self.head.next # 生成一个外部引用指向首元结点
while curnode is not None: # 顺链域往下搜索,直到curnode为空
if curnode.data == value:
return True # 搜索的数据在链表中
curnode = curnode.next
return False # 搜索的数据不在链表中
# 创建my_list链表
my_list = Linklist()
# 查看此时链表是否空
print(my_list.is_empty()) # 输出:True
my_list.printf() # 输出:链表中没有元素
# 添加结点
my_list.append(10)
my_list.append(20)
my_list.append(30)
my_list.append(40)
my_list.append(50)
my_list.printf() # 输出:10 20 30 40 50
# 插入结点
my_list.insert(2, 60)
my_list.printf() # 输出:10 60 20 30 40 50
# 删除结点
my_list.remove(20)
my_list.printf() # 输出:10 60 30 40 50
# 获取列表中第二个结点的数值域
print(my_list.get_data(2)) # 输出:60
# 查询60,100是否存在于链表中
print(my_list.search(60)) # 输出:True
print(my_list.search(100)) # 输出:False
# 查询链表中元素个数
print(my_list.size()) # 输出:5
欢迎大家阅读完文章后进行指正^0^