线性表
元素间具有线性关系的一种线性结构
由n个具有相同数据类型的数据元素构成的有限序列
python中的内置类型list和tuple可以看作线性表的实现
线性表的存储和实现
基于顺序存储
把线性表的所有元素按照其逻辑顺序依次存储到计算机的内存单元中一块连续的存储空间
元素在内存中的物理存储次序和它们在线性表中的逻辑次序一致
顺序表的基地址和数据元素所占存储空间的大小即可计算出第i个数据元素的地址
存取元素的时间复杂度为0(1),顺序表是按照数据元素的位序号随机存取的结构
特点
在逻辑上相邻的元素在物理存储位置上也相邻
可安装数据元素的位序号进行随机存取
进行插入删除需要移动大量的元素
需要进行存储空间的预先分配,可能会造成空间浪费,但存储密度高
代码
class SqList:
def __init__(self, maxsize):
self.curLen = 0 # 顺序表的长度
self.maxSize = maxsize # 顺序表的最大长度
self.listItem = [None] * self.maxSize # 顺序表存储空间
# self.listItem = [] * self.maxSize# 顺序表存储空间
def isEmpty(self):
"""判断线性表是否为空表"""
return self.curLen == 0
def length(self):
"""返回线性表的长度"""
return self.curLen
def get(self,i):
"""读取并返回线性表中的第i个数据元素"""
# 剔除脏数据
if i < 0 or i > self.curLen -1: # 数组是从0开始计数
return Exception('第i个元素不存在')
return self.listItem[i]
def clear(self):
"""将线性表置成空表"""
self.curLen = 0
def add(self, value):
for j in range(self.curLen, 0, -1):
self.listItem[j] = self.listItem[j - 1]
self.listItem[0] = value
self.curLen += 1
# 我看了很多博客,发现有的人使用了python自带的列表来完成相关操作,这里做个了解吧,毕竟有列表的话,直接用列表了,就不需要在这么麻烦的实现了
def insert(self,i,x):
"""插入x作为第i个元素"""
# 插入前先判断表满了没有
if self.curLen == self.maxSize:
raise Exception('顺序表满')
# 数据表如果没满,剔除脏数据
if i < 0 or i > self.curLen:
raise Exception('插入位置非法')
# 因为插入的是第i个位置,先要找到i,因为数组是从0开始计数,所以第i个位置实际上是i-1
# 从后往前找,找到位置之后从后往前到要插入的位置向后移动一格
for j in range(self.curLen,i-1,-1):
self.listItem[j] = self.listItem[j-1]
# 要插入的位置将要插入的元素插入进去
self.listItem[i] = x
# 插入成功之后长度+1,ok
self.curLen += 1
def remove(self,i):
"""删除第i个元素"""
# 剔除脏数据
if i < 0 or i > self.curLen -1:
raise Exception('删除位置非法')
# 从删除的位置开始,然后将后面的元素挨个往前移动一格
for j in range(i,self.curLen):
self.listItem[j] = self.listItem[j+1]
# 删除完毕长度减1
self.curLen -= 1
def indexOf(self,x):
"""返回元素x首次出现的位序号"""
# 直接遍历数组即可
for i in range(self.curLen):
if self.listItem[i] == x:
return i
return -1
def show(self):
# for i in range(self.curLen):
# print(self.listItem[i],end='')
for i in range(0, self.curLen):
if i != self.curLen - 1:
print(self.listItem[i], end=',')
else:
print(self.listItem[i], end='')
if __name__ == '__main__':
s = SqList(5)
s.add(1)
s.add(2)
s.insert(2,6)
s.insert(1,8)
s.remove(0)
print(s.indexOf(3))
s.show()
小练习
求字母a-b任意前驱节点和后继节点
def change_letters(self,val):
"""根据输入的值返回前驱和后继"""
# 先排序,不然不好取,调用sort方法已经排好序了,直接开写
# temp_lst = self.listItem.sort()
# print(self.listItem)
temp_num = self.indexOf(val)
print(temp_num)
if temp_num < 0 or temp_num > self.maxSize:
return -1
if temp_num == 0:
return f'前驱节点为None,后继节点为b'
if temp_num == 25:
return f'前驱节点为y,后继节点为None'
if temp_num > 0 and temp_num < 25:
return f'前驱节点为{self.listItem[temp_num - 1]},后继节点为{self.listItem[temp_num + 1]}'
基于链式存储
基于链式存储的线性表称为链表
链表用若干地址分散的存储单元存储数据元素
逻辑上相邻的数据元素在物理位置上不一定相邻
必须采用附加信息表示数据元素之间的逻辑关系
链表:数据域,指针域
单链表
单链表是指节点中只包含一个指针域的链表
单链表的节点只有值跟指向下一个节点的指针
单链表节点的存储空间是在插入和删除过程中动态申请和释放的,不需要预先分配
# 定义节点
class ListNode(object):
def __init__(self, x):
# 数据域:存储数据元素
self.val = x
# 指针域:存储下一节点的指针
self.next = None
每个单链表有一个头节点,空链表除外,尾节点指向None值
# 定义单链表
class SingleLinkList(object):
# 定义头节点,空链表默认为空
def __init__(self,node = None):
# 首地址指针
self.__head = node
# 判断单链表是否为空
def is_empty(self):
return self.__head == None
# 计算链表的长度
def length(self):
# 判断链表是否为空
if self.is_empty():
return 0
# 创建一个游标,用来移动,先指向头节点
cur = self.__head
# 记录节点数量
count = 0
while cur != None:
count += 1
cur = cur.next
return count
# 单链表头部添加节点
def add(self,item):
# 将数据放入节点
node = ListNode(item)
# 头节点变为当前节点的下一个节点
node.next = self.__head
# 将当前节点变为头节点
self.__head = node
# 单链表尾部添加节点
def append(self,item):
# 将数据放入节点
node = ListNode(item)
# 如果节点为空,直接放入头节点
if self.is_empty():
self.__head = node
else:
# 找到头节点
cur = self.__head
# 找到最后一个节点
while cur.next != None:
cur = cur.next
# 找到最后一个节点之后,最后一个节点指向要插入的节点
cur.next = node
# 指定位置插入节点
def insert(self,pos,item):
# 如果位置小于等于0,用头插法
if pos <= 0:
self.add(item)
elif pos > self.length() - 1:
# 如果比当前链表都要长,用尾插法
self.append(item)
else:# 其余正常查找然后插入
# 找到头节点
cur = self.__head
count = 0
while count < pos - 1:
count += 1
cur = cur.next
# 找到要插入的位置之后,开始插入
node = ListNode(item)
# 当前节点的指向变为节点的指向
node.next = cur.next
# 当前节点的指向变为要插入的节点
cur.next = node
# 删除节点
def remove(self,item):
if self.is_empty():
return False
# 指定游标
cur = self.__head
#
pre = None
while cur != None:
# 判断该节点是不是头节点
if cur.val == item:
if cur == self.__head:
self.__head = cur.next
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
# 查找节点
def search(self,item):
# 从头节点开始查找
cur = self.__head
# 如果头节点存在,进行循环
while not cur:
# 判断值是否相等
if cur.val == item:
return True
else:
cur = cur.next
return False
# 遍历单列表
def travel(self):
cur = self.__head
while cur != None:
print(cur.val)
cur = cur.next
# 将链表倒叙加入字典
def printListFromTailToHead(self):
result_array = []
# 拿到头节点
cur = self.__head
while cur != None:
result_array.insert(0,cur.val)
cur = cur.next
return result_array
循环链表
将链表的首尾相连
尾节点的指针域指向头节点的指针
判断是否是循环链表只需要看他的尾节点是否指向头节点即可
一般循环链表使用尾指针来进行表示
双向链表
双向链表的节点具有两个指针域,一个指向前驱节点,一个指向后继节点
双向链表的插入删除需要修改两个指针域
class DuLNode:
def __init(self,data,prior,next):
self.data = data # 数据域
self.prior = prior # 前驱指针
self.next = next # 后继指针
总结
顺序表不便于进行插入删除,但是可以进行高效存取
链表插入删除效率高,但是按照位序号随机存取麻烦
线性表顺序存储存取时间复杂度位O(1),插入删除时间复杂度位O(n)
链表访问时间复杂度位O(n),插入删除为O(1)
参考资料
《数据结构(Python版)》作者:吕云翔、郭颖美、孟爻
这两个可以在线模拟数据结构
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
https://visualgo.net/zh