数据结构: 定义: 特定的数据类型(个体)和特定的存储结构(个体的关系) 数据如何存储在内存中 分类: 线性结构: 数据元素之间存在一对一的线性关系。线性结构拥有两种不同的存储结构,即顺序存储结构和链式存储结构。 数组与列表:顺序存储结构 相同点: 需要申请一块连续的内存空间 不同点: 列表或者数组中的元素是如何存储的,以及两者的区别? 在其他语言中(C/C++)数组申请的内存空间是一定的, 比如申请10M的内存空间,如果数据超过10M会溢出,需要手动申请和释放 在python中不会出现溢出报错这种问题,底层做了处理会自动扩展,不需要手动申请和释放 python: li = [1,2,3,4,54,56,56,67,77] C/C++: malloc() 申请 free() 释放 列表提供了哪些最基本的操作? append(), insert(), pop()等 这些操作的时间复杂度是多少? 插入时间复杂度:O(n) 因为数组是连续存放的,后面的数据会后移 查找时间复杂度: O(1) 按照索引运算查找即可 为啥数组(列表)的索引或者下标是从0开始? 减少运算的压力 查找内存位置的时候直接按照索引算即可,如果从1开始还要减一 关于数组(列表)的优缺点: 优点: 查找快 缺点: 插入慢 要求一块 连续 的内存空间 *** 链表:链式存储结构 插入时间复杂度:O(1) 查找时间复杂度: O(n) 链表的优点: 1、不需要一块连续内存空间 2、相对于数组来说,插入较简单 单链表 双链表 循环链表 约瑟夫问题 链表算法:增删改查以及总长度 应用: 栈 队列 非线性结构: 树作为一种应用广泛的一对多非线性数据结构,不仅有数据间的指向关系,还有层级关系 如何在内存中表示呢? 树 图 图论
########################### # 单链表:水浒英雄排序 # 双向链表多一个pre class Hero: def __init__(self,name=None,no=None,nickname=None,pNext=None): self.name = name self.no = no self.nickname = nickname self.pNext = pNext # 直接添加到尾部 def add(head,pnew): cur = head while cur.pNext != None: cur = cur.pNext cur.pNext = pnew # 指定位置进行添加 def insert(head,pnew): cur = head while cur.pNext != None: if cur.pNext.no > pnew.no: break cur = cur.pNext pnew.pNext = cur.pNext cur.pNext = pnew # 根据no删除某个节点(英雄) def delHero(head,no): cur = head while cur.pNext != None: if cur.pNext.no == no: break cur = cur.pNext else: print("没有此元素") cur.pNext = cur.pNext.pNext # 查看单链表是否为空 def is_empty(head): if head.pNext == None: return True else: return False # 计算单链表长度 def length(head): cnt = 0 cur = head while cur.pNext != None: cur = cur.pNext cnt += 1 return cnt # 打印所有的单链表数据 def getAll(head): cur = head while cur.pNext != None: cur = cur.pNext print('编号是:%s,姓名是:%s,外号:%s' %(cur.no,cur.name,cur.nickname)) head = Hero() h1 = Hero('宋江',1,'及时雨') add(head,h1) h2 = Hero('卢俊义',2,'玉麒麟') add(head,h2) h3 = Hero('吴用',3,'智多星') add(head,h3) h4 = Hero('马波',5,'波神') add(head,h4) h5 = Hero('波老师',4,'波波') add(head,h5) print(length(head)) getAll(head) ###########################
########################### # 循环链表:解决约瑟夫问题 # 设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数, # 数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列 class Child: first = None def __init__(self,data = None,pNext = None): self.data = data self.pNext = pNext # 生成n个小朋友,构成循环链表 def add(self,num): cur = None for i in range(num): child = Child(i+1) if i == 0: # 当i=0时,构成一个循环链表,指向自己,目的是:后面的好加入 self.first = child #first指向第一个孩子对象 self.first.pNext = self.first #第一个孩子的指针指向自己构成循环链表 cur = self.first else: cur.pNext = child child.pNext = self.first cur = cur.pNext # 展示所有的小孩 def showAll(self): cur = self.first #第一个孩子节点 while cur.pNext != self.first: #只有末尾的节点指针指向第一个节点 print('小孩的编号是:%s' %cur.data) cur=cur.pNext print('小孩的编号是:%s' %cur.data) #打印末尾节点的编号 # 解决约瑟夫问题 def countChild(self,m,k): tail = self.first #第一个孩子节点, while tail.pNext != self.first: #除了最后一个节点外,都是True tail = tail.pNext # 退出循环的话,已经到了最后一个小朋友,就在first后面 # tail是用来出列之后修复循环链表,永远在first后面,执行完上面的代码就在first后面了 # 从哪里开始数,first移动到报数位置,tail在first后面 for i in range(k-1): self.first = self.first.pNext tail = tail.pNext while tail != self.first: # 退出循环的时候,圈子里面只剩一个人 # 数2下,first、tail前移1下 # 数3下,first、tail前移2下 # 数m下,first、tail前移m-1下 for i in range(m - 1): self.first = self.first.pNext tail = tail.pNext # 修复循环链表 self.first = self.first.pNext tail.pNext = self.first print('留在圈子里面的小孩编号:%s' %tail.data) child = Child() #生成对象 child.add(1000) #添加100个小孩 # child.showAll() #打印100个小孩的编号 child.countChild(1000,300) #从编号为3的人开始,数到80的人出列 ###########################