数据结构与算法实验练习(二)(排序及线性表的应用)

数据结构与算法分析课下实验练习,现记录一下解答过程,欢迎大家批评指正。

声明:本题目来源于西安交通大学电信学院原盛老师,任何单位或个人在使用、转载或引用本题目时,请务必标明出处为“西安交通大学电信学院原盛老师”。

练习二:线性表基本训练


线性表是其他复杂关系数据结构的基础,尤其是对掌握链式存储技术更功不可没。

(1) 参照教材中的 ListADT 的定义,分别实现顺序线性表、带头节点的单向链表的线性表和带头节点的单向循环链表的线性表。

(2) 设置一组测试,验证 (1) 中实现的三个数据结构都可以正确完成 ADT 中所规定的逻辑。

(3) 设计一个表格,列出三个不同数据结构在各个行为上的时间复杂度。

(4) 用线性表解决约瑟夫环问题 这个问题源自约瑟夫斯·弗拉维乌斯,一位公元 1 世纪的罗马历史学家。故事略显血腥。41 名叛军被困在一个山洞里,被敌军包围。他们决定集体自杀:他们围成一圈,按顺序每隔一人处决一人,如此循环往复,直到只剩下最后一名叛军——据称他会自行了断(但这希望渺茫)。谁会是那位幸存者呢?

数学上,我们可以将问题简化为:有 n 个人,从第 1 个人开始编号到 n,每个人按照 1 到 m 的顺序报数,每数到 m 的人会被淘汰,问最后留下的人的编号是多少?解决约瑟夫环问题有多种算法,其中包括递推公式法、递归法以及利用循环链表等数据结构的解法。

请同学们使用任务 2 中的基于头结点的单循环链表的线性表编写解决约瑟夫环问题的算法。该算法的输入为人数 n 和间隔数 m,算法的输出为剩下的唯一编号。

请绘制一个图(横坐标为人数 n,纵坐标为编号),在间隔为 2 的情况下,绘制不同人数下对应的留下的唯一编号是什么?(0 < n ≤ 200)根据图形,找一找规律。

请给出这个算法的时间复杂度。


第一问

三种数据类型的定义。

顺序线性表:

#顺序线性表
class AList:def __init__(self,size):'''Alist有4个数据成员,包括存储线性表的数组listArray,表的最大长度msize,当前实际长度numInList和当前元素在数组中的位置curr'''self._listArray=[None]*sizeself._msize=sizeself._numInList=0self._curr=0#接下来添加一系列方法#首先添加清空方法def clear(self):self._numInList=0self._curr=0#添加插入方法,这里需要额外的参数:插入的数值def insert(self,item):#首先判断表是否已满if self._msize==self._numInList:return print('当前表已满,无法再插入元素')for i in range(self._numInList,self._curr,-1):self._listArray[i]=self._listArray[i-1]self._listArray[self._curr]=itemself._numInList+=1#添加append方法def append(self,item):#同样,首先判断表是否已满if self._msize==self._numInList:return print('当前表已满,无法再追加元素')self._listArray[self._numInList]=itemself._numInList+=1#判断表是否为空def isEmpty(self):if self._numInList==0:return Trueelse: return False#判断curr所指元素是否存在当前表中def isInlist(self):if self._curr<=self._numInList-1 and self._curr>=0:return Trueelse: return False#添加remove方法def remove(self):if self.isEmpty():return print('当前表为空,无法删除元素')#判断curr值是否合法if not self.isInlist():return print('当前curr值不合法')it=self._listArray[self._curr]for i in range(self._curr,self._numInList-1):self._listArray[i]=self._listArray[i+1]self._numInList-=1return it#添加setFirst方法def setFirst(self):self._curr=0#添加next方法def next(self):if self._curr<self._msize-1:self._curr+=1else:return print('当前指针在尾部,无法向后移动')#添加prev方法def prev(self):if self._curr>0:self._curr-=1else:return print('当前指针在头部,无法向前移动')#添加length方法def length(self):return self._numInList#添加setPos方法def setPos(self,pos):self._curr=pos#添加setValue方法def setValue(self,item):if self.isInlist():self._listArray[self._curr]=itemelse:return print('当前指针位置不在表内,无法重置元素')#添加currValue方法def currValue(self):if self.isInlist():return self._listArray[self._curr]else: return print('当前指针位置不在列表内,无法返回')#添加print方法def print(self):if self.isEmpty():return print('这个数组是空的')else:print('[',end=' ')for i in range(0,self._numInList):print(self._listArray[i],end=' ')print(']')

带头节点的单向链表:

import random
#创建一个创建节点的类
class Node:def __init__(self,data=None,next=None):self.data=dataself.next=next#创建带有哑结点的单链表
class Llist:def __init__(self):'''带有哑结点的单链表有三个 数据成员:分别为head头结点,tail尾节点,和curr指针'''#先创建哑结点dum=Node()self.head=dumself.tail=self.headself.curr=dum#创建一个clear方法def clear(self):self.tail=self.head#setFirst方法def setFirst(self):self.curr=self.headreturn self.curr#next方法def next(self):#首先判断此时它是否是尾节点if self.curr==self.tail:print('此时节点已指向尾节点,无法next')else:self.curr=self.curr.nextreturn self.curr#创建insert方法  插入实际上是向后插入一个元素def insert(self,item):#先创建一个节点用于放置插入元素newnode=Node()newnode.data=item#判断curr是否是在尾节点上,如果是的话要移动尾节点if self.curr==self.tail:newnode.next=self.curr.nextself.curr.next=newnodeself.tail=newnodeelse:newnode.next=self.curr.nextself.curr.next=newnode#创建删除操作  curr指向的元素并非要删除的元素,删除的是curr后边的元素def remove(self):if self.curr==self.tail:return print('当前元素不能删除')temp=self.curr.next.nextself.curr.next=Noneself.curr.next=tempif self.curr.next ==None:self.tail=self.curr#创建append方法def append(self,item):self.curr=self.tailself.insert(item)#创建prev方法def prev(self):temp=self.currself.setFirst()while(self.curr.next!=temp):self.curr=self.curr.nextreturn self.curr#创建length方法def length(self):'''一般而言,如果链表带有哑结点,那么length是不包括哑结点这个节点的'''i=0self.curr=self.headwhile(self.curr!=self.tail):i+=1self.next()return i#创建setValue方法def setVaule(self,item):self.curr.data=itemdef currValue(self):return self.curr.datadef isEmpty(self):return self.head==self.taildef isInlist(self,item):self.setFirst()while(self.curr!=self.tail):if self.curr.data==item:return Trueself.curr=self.curr.nextdef print(self):self.setFirst()self.next()while(self.curr!=self.tail):print(self.curr.data,end=' ')self.next()print(self.curr.data)

单向循环链表:单向循环链表和普通的单向链表相差不大。只需在创建时,让哑结点指向自己即可。同时,在一些方法上可能有些修改。其他相差不大。

#单向循环列表我们在课堂上并没有学过,但是其和不循环的单向列表相差不大
#同样,创建一个创建节点的类
class Node:def __init__(self,data=None,next=None):self.data=dataself.next=next#创建带有哑结点的单链表
class Llist1:def __init__(self):#先创建哑结点dum=Node()#因为是循环的,所以我们让哑结点指向自己dum.next=dumself.head=dumself.tail=dumself.curr=dum#创建一个clear方法def clear(self):self.tail=self.head#setFirst方法def setFirst(self):self.curr=self.headreturn self.curr#next方法def next(self):self.curr=self.curr.nextreturn self.curr#创建insert方法  插入实际上是向后插入一个元素def insert(self,item):#先创建一个节点用于放置插入元素newnode=Node()newnode.data=item#判断curr是否是在尾节点上,如果是的话要移动尾节点      newnode.next=self.curr.nextself.curr.next=newnodeif self.curr==self.tail:self.tail=newnode#创建删除操作  curr指向的元素并非要删除的元素,删除的是curr后边的元素def remove(self):if self.curr==self.tail:return print('当前元素不能删除')temp=self.curr.next.nextself.curr.next=Noneself.curr.next=tempif self.curr.next ==self.head:self.tail=self.curr#创建append方法def append(self,item):self.curr=self.tailself.insert(item)#创建prev方法def prev(self):temp=self.currself.setFirst()while(self.curr.next!=temp):self.curr=self.curr.nextreturn self.curr#创建length方法def length(self):'''一般而言,如果链表带有哑结点,那么length是不包括哑结点这个节点的'''#调用length后要让curr指针指向原来的位置temp=self.curri=0self.curr=self.headwhile(self.curr!=self.tail):i+=1self.next()self.curr=tempreturn i#创建setValue方法def setVaule(self,item):self.curr.data=itemdef currValue(self):return self.curr.datadef isEmpty(self):return self.head==self.taildef isInlist(self,item):self.setFirst()while(self.curr!=self.tail):if self.curr.data==item:return Trueself.curr=self.curr.nextdef print(self):self.setFirst()self.next()while(self.curr!=self.tail):print(self.curr.data,end=' ')self.next()print(self.curr.data)

第二问

测试创建的结构类型是否符合逻辑。

from 单链表 import Llist
from 单向循环链表 import Llist1
from 顺序表 import AList
import random
#测试这三种数据结构的逻辑
#从增删改查几方面
#数组实现的顺序表array=AList(10)
for i in range(5):array.append(i)
print(f'当前指针所指的数据是:{array.currValue()}')#顺序表的append并不改变curr指针的位置
print('看看数组里有哪些成员:',end='')
array.print()
print(f'这个数组的长度是:{array.length()}')
array.setFirst()
array.setValue(8)
print('修改后的数组为',end=':')
array.print()
print(f'这个元素为空吗?  {array.isEmpty()}')
print('现在我插入88,看看插入后的结果:',end='')
array.insert(88)
array.print()
print('我现在设置curr指向array[5],首先看一下curr有没有指向有效元素',end=',')
array.setPos(5)
print(array.isInlist())
print('然后删除这个位置上的元素,再插入88,看看结果是',end=':')
array.remove()
array.insert(88)
array.print()
print('现在我要清空了,看看结果:',end='')
array.clear()
array.print()
print('\n\n')
# 单向链表
li=Llist()
for i in range(10):li.append(i)
print('看看链表里有哪些成员:',end='')
li.print()#print方法后curr指针就指向了尾节点
print(f'当前链表所指向的元素是:{li.currValue()}')
print(f'链表的长度是:{li.length()}')
li.setFirst()
li.next()
li.next()
li.next()
li.remove()
print(f'看看删除之后的链表:',end='')
li.print()
print('\n\n')
#单向循环列表
li1=Llist1()
for i in range(10):li1.append(i)
print('看看链表里有哪些成员:',end='')
li1.print()#print方法后curr指针就指向了尾节点
print(f'当前链表所指向的元素是:{li1.currValue()}')
print(f'链表的长度是:{li1.length()}')
li1.setFirst()
li1.next()
li1.next()
li1.next()
li1.remove()
print(f'看看删除之后的链表:',end='')
li1.print()
#看看循环功能是否正常
for i in range(random.randint(1,100)):li1.next()
print(f'我要循环一下,看看当前指针所指的数据是:{li1.currValue()}')

结果如下:

第三问

通过索引查找

插入

删除

前驱

顺序表

O(1)

O(n)

O(n)

O(1)

单向链表

O(n)

O(1)

O(1)

O(n)

单向循环列表

O(n)

O(1)

O(1)

O(1)

第四问

利用第一问实现的单向循环列表模拟这个过程,每次淘汰掉一个人就作一个删除操作, 直到最后留下的一个人输出即可。

在这个过程中,我们用数字来模拟人,最后输出的是一个数字。

这里需要注意一个问题,第一问实现的单向循环链表是有头结点的,所以在模拟淘汰的时候需要注意避开这个头结点。即有两种情况:第一,在报数的时候遇到头结点就跳过。第二,如果报数结束刚好到了这个头结点, 那么也需要避开。

导入的文件名大家要根据自己情况修改。

from 单向循环链表 import Llist1
import matplotlib.pyplot as plt
#循环列表解法
#假设有n个人
#假设每隔m个人就淘汰  如果是报数的话,就要减一
def ysf(n,m):'''这里解释一下两个参数的意义,n表示有n个人,m表示每隔m个人就淘汰一个人。但是这里的m含义和题中的m含义有所不同:题中的m是报数报到m的那个人就淘汰,而这里的m是间隔m个,所以两者会相差1,输入时要记得转换一下即可'''peo=Llist1()for i in range(n):peo.append(i+1)#从一号开始peo.setFirst()while(peo.head.next!=peo.tail):for i in range(m):peo.next()if peo.curr.data==None:peo.next()if peo.curr==peo.tail:peo.next()peo.remove()return peo.tail.datay=[]
for i in range(1,201):y.append(ysf(i,1))
print(y)
plt.figure()
plt.plot(range(1,201),y)
plt.show()

结果展示:

规律为: f(n,m)=[f(n-1,m)+m]%n+1

时间复杂度为: O(n)

注:这里大家看情况修改自己的代码,因为每个人对隔m个人淘汰一个人的理解不一样(或者是报数到m就把那个人淘汰掉)这些细节都需要自己修改。同样规律的总结也会有所不同。

作者自己编写,如果错误,请不吝赐教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/58604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于回溯与分支限界的一些介绍

这篇文章将介绍回溯算法与分支限界算法的有关概念、具体应用及代码等内容。 一、回溯法 1.1 概念 回溯法是一种试探法&#xff0c;所以它也叫试探算法。它尝试构建问题的解&#xff0c;并且在发现解不满足条件的时候撤销选择&#xff08;即“回溯”&#xff09;&#xff0c;…

day05-Filebeat扩展

Filebeat对接ES加密集群 #查看集群状态 [09:22:25 rootelk1:~]#curl 10.0.0.91:9200/_cat/nodes -u elastic:1234561.编写配置文件 [09:52:06 rootelk3:/etc/filebeat]#vim 12-log-to-es_tls.yamlfilebeat.inputs: - type: logpaths:- /var/log/nginx/access.log*json.keys_un…

Vue全栈开发旅游网项目(3)-Vue路由配置

1. 配置路由 1.1 导入路由工具 文件地址&#xff1a;src\router\index.js import { createRouter, createWebHistory } from vue-router//导入配置路由的工具 import HomeView from ../views/HomeView.vue//导入组件const router createRouter({//创建路由对象history: cre…

代随(136):图论dfs——邻接矩阵

题干&#xff1a; 代码&#xff1a; #include <iostream> #include <vector> using namespace std;vector<vector<int>>res; vector<int>path;void dfs(vector<vector<int>>&graph, int x, int n) {if(x n){res.push_back(pat…

精选录屏软件大赏:满足不同场景需求的录制利器

现在是互联网的世界&#xff0c;所以很多时候学习、工作我们都离不开电脑。在我们通过互联网查看网课、开网络会议的时候是不是会因为速度太快而来不及记录&#xff1f;那我推荐使用一些windows录屏软件来记录所有重点&#xff0c;方便我们后期复盘。 1.FOXIT录屏大师 链接&a…

【python】OpenCV—findContours(4.5)

文章目录 1、功能描述2、原理分析3、代码实现4、效果展示5、完整代码6、参考 1、功能描述 输入图片&#xff0c;计算出图片中的目标到相机间的距离 2、原理分析 用最简单的三角形相似性 已知参数&#xff0c;物体的宽度 W W W&#xff0c;物体到相机的距离 D D D&#xff0…

【华为HCIP实战课程二十八】中间到中间系统协议IS-IS邻居关系排错,网络工程师

一、ISIS邻居关系条件 1、同一层次(比如Level-2路由器不能和Level-1路由器形成邻居关系) 2、同一区域(L1必须同一区域) 3、同一网段 R1和R2之间分别配置如下IP地址和掩码: R1 的接口S1/0/0掩码为/24 R2的接口S1/0/0配置成掩码/28: 此时R1和R2依然可以建立ISIS邻居关系…

文件操作和 IO(一):文件基础知识 文件系统操作 => File类

目录 1. 什么是文件 1.1 概念 1.2 硬盘, 内存, 寄存器之间的区别 1.3 机械硬盘和固态硬盘 2. 文件路径 2.1 绝对路径 2.2 相对路径 3. 文件分类 4. File 类 4.1 属性 4.2 构造方法 4.3 方法 1. 什么是文件 1.1 概念 狭义上的文件: 保存在硬盘上的文件广义的上的文…

Flutter Color 大调整,需适配迁移,颜色不再是 0-255,而是 0-1.0,支持更大色域

在之前的 3.10 里&#xff0c; Flutter 的 Impeller 在 iOS 上支持了 P3 广色域图像渲染&#xff0c;但是当时也仅仅是当具有广色域图像或渐变时&#xff0c;Impeller 才会在 iOS 上显示 P3 的广色域的颜色&#xff0c;而如果你使用的是 Color API&#xff0c;会发现使用的还是…

web自动化测试平台开发之核心执行器

web自动化测试平台开发之核心执行器 一、如何从自动化框架到核心执行器二、核心执行器框架逻辑梳理三、核心执行器利用命令驱动执行 一、如何从自动化框架到核心执行器 脚本:底层用了三个内容:pythonpytestselenium&#xff0c;线性脚本&#xff0c;只是单纯的把功能测试用例转…

Rust 力扣 - 2090. 半径为 k 的子数组平均值

文章目录 题目描述题解思路题解代码题解链接 题目描述 题解思路 半径为 k 的子数组平均值 等价于 子数组长度为2 * k 1的总和 除于 2 * k 1 我们遍历长度为2 * k 1的窗口&#xff0c;我们只需要记录窗口内的平均值即可 题解代码 impl Solution {pub fn get_averages(num…

直接内存、死锁、方法句柄

直接内存 1. 不是虚拟机运行时数据区的一部分&#xff0c;也不是《Java虚拟机规范》中定义的内存区域 2. 直接内存是在Java堆外、直接向系统申请的内存区间 3. 来源于NIO,通过存在堆中的DirectByteBuffer操作Native内存 4. 通常&#xff0c;访问直接内存的速度会优于Java堆&am…

C++《list》

在本篇当中我们将学习STL中的list&#xff0c;在此list就是我们之前在数据结构学习过的链表&#xff0c;在本篇中我们要来了解list当中的成员函数该如何使用&#xff0c;由于list各个函数的接口和之前学习过的vector类型&#xff0c;因此在学习list的使用就较为轻松。在lis篇章…

【牛客刷题实战】二叉树遍历

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 牛客题目&#xff1a; 二叉树遍历 题目描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解题思路 问题理解 算法选择 具体思路 解题要点 完整代码&#xff08;C语言&#xff09; 兄弟们共勉 &#xff01;&…

vmvare启动freebsd操作系统密码忘记了怎么办?

本章教程,主要介绍,通过vmvare安装的freebsd操作系统,密码忘记了,如何重置密码。 一、重启虚拟机 在重启过程中,按键盘中是数字2,进入单用户模式。 二、进入到shell界面 在出现“Enter full pathname of shell or RETURN for /bin/sh:”直接按回车键。 三、输入命令 mou…

【设计模式系列】代理模式(八)

一、什么是代理模式 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它为其他对象提供一种代理以控制对这个对象的访问。代理模式在不直接访问实际对象的情况下&#xff0c;提供了对目标对象的间接访问。通过引入一个代理对象来间接操作实际对…

WPS查询函数VLOOKUP,匹配寻找值自动带入值

想实现在下表输入物料名称后&#xff0c;把上表中的单位自动带入 那就要用到VLOOKUP函数&#xff0c;获取第2个表第1列的值后去第1个表的第1列匹配&#xff0c;匹配到后得到行数值&#xff0c;把第1个表的第2列赋值给第2个表的第2列。 Vlookup函数参数为Vlookup(查找值&#…

sqoop问题汇总记录

此篇博客仅记录在使用sqoop时遇到的各种问题。持续更新&#xff0c;有问题评论区一起探讨&#xff0c;写得有不足之处见谅。 Oracle_to_hive 1. main ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTr…

简单说明vuex

vuex 知识结构配置调用 知识结构 vue用于管理公共数据的仓库 配置 state&#xff1a;所有公共数据的初始状态&#xff08;初始值&#xff09; export default {state: {count: 0,} };mutations&#xff1a;修改state内容的方法&#xff08;必须为同步方法&#xff09; export …

分类算法——决策树 详解

决策树的底层原理 决策树是一种常用的分类和回归算法&#xff0c;其基本原理是通过一系列的简单决策&#xff0c;将数据集划分为多个子集&#xff0c;从而实现分类。决策树的核心思想是通过树形结构表示决策过程&#xff0c;节点代表特征&#xff0c;边代表决策&#xff0c;叶子…