1有序队列(10分)
题目内容:
一开始给出了一个由小写字母组成的字符串 S。我们规定每次移动中,选择最左侧的字母,将其从原位置移除,并加到字符串的末尾。这样的移动可以执行任意多次
返回我们移动之后可以拥有的最小字符串(注:在Python3中,字符串的大小可用不等号比较)。
输入格式:
S。S为仅含有小写字母的字符串,长度不超过100000。
输出格式:
一个与S等长的字符串。
输入样例:
“cba”
输出样例:
acb
分析:题目规定 “每次移动中,选择最左侧的字母,将其从原位置移除,并加到字符串的末尾”,这样的操作可以让人联想到 队列的属性:FIFO。
解题思路:
1.先定义队列的类(注意根据题目要求定义:列表左侧为队首(出队),列表右侧为队尾(进队))
2.将目前的字符串设置为最小字符串
3.利用队列对字符串进行变形操作,将每次变形后的字符串,与当前最小字符串比较,如果比当前最小字符串小,则替换最小字符串。
4.利用count计数,总共迭代len(S)-1次,将所有元素都排进队尾一次,形成的字符串全部比较一次。
class Queue(list):
def isEmpty(self):
return self==[]
def enqueue(self,item):
self.append(item)
def dequeue(self):
return self.pop(0)
def peek(self):
return self(-1)
def size(self):
return len(self)
def func(S):
q=Queue()
count=0
min_str=S #将当前的字符串设置为最小字符串
for c in S: #字符串中的所有元素进队
q.enqueue(c)
while count< q.size()-1: #总共迭代变换len(S)-1次
q.enqueue(q.dequeue()) #将队列最左侧的字符 从原位置移动到队列最右(末尾)
new_str="".join(q)
if new_str < min_str: #与当前最小字符串比较,如果比当前最小字符串小,则替换之
min_str = new_str
count+=1 #迭代一次,计数器+1
return min_str
S = eval(input())
print(func(S))
上面这种做法用到了队列的类,但是测试用例4因为运行时间超出,没有通过。
我在CSDN里面查了别人的作业,人家用的字符串切片的方法,优化了运行时间,还是同样的思路,只是把字符串直接当成队列来操作。
def func(S):
min_str=S
for i in range(len(S)):
S=S[1:]+S[0]
if S < min_str: #与当前最小字符串比较,如果比当前最小字符串小,则替换之
min_str = S
return min_str
S = eval(input())
print(func(S))
测试样例全部通过。
2最近的请求次数(10分)
题目内容:
计算每个事件发生之时,往前算10000毫秒内有多少个事件发生,包含当事件;也即对于列表中的每个元素k,算出整个列表中有多少个元素介于k-10000和k(两端均含)之间。
输入格式:
一个已排序列表mylist,所有元素为非负整数,记录各个请求的发生时间,单位为毫秒。
输出格式:
一个与mylist等长的列表。
输入样例:
[0,10,100,1000,10000,20000,100000]
输出样例:
[1,2,3,4,5,2,1]
解题方法:
解题方法1:一开始我是想到遍历列表的方法来做的,对列表中的每一个元素都进行以下操作:
把列表中的元素与当前元素一一比较,符合条件的话计数器+1,最后用列表的append方法,把计数器加进去。
A=[] #创建计数列表
def func(mylist):
for k in mylist: #对列表中的每一个元素都操作一遍
count=0
for j in mylist: #把列表中的每一个元素都比一遍
if k-10000<= j <= k:
count+=1 #列表中出现一个满足条件的元素,计数器+1
A.append(count)
return A
mylist = eval(eval(input()))
print(func(mylist))
但我忽略了这是一个已排序列表,我的这种解法时间复杂度太大O(n**2),并且没用到任何队列的知识。
解题方法2:
分析:这是一个已排序列表,右边的值都比左边的大。那么在单次循环中,右边的值都不会比当前循环的元素小,不满足i<=k的条件,我们可以省去这部分操作,不去判断当前元素右侧的大值。
我们还需要判断,i>=k-10000的条件,如果列表中的第一个元素不满足,那我们把第一个元素去掉,接着继续判断第二个元素,依次类推,直到列表左侧的元素满足条件。这样,列表中剩下的元素的个数,就是对当前循环元素来说,符合条件的元素个数。
默认不用判断当前元素的右侧元素。
当前循环元素:0,列表是[0],满足条件0>=0-10000,结果为[1]。
当前循环元素:10,列表是[0,10],满足条件0>=10-10000,结果为[1,2]
当前循环元素:100,列表是[0,10,100],满足条件0>=100-10000,结果为[1,2,3]
当前循环元素:1000,列表是[0,10,100,1000],满足条件0>=1000-10000,结果为[1,2,3,4]
当前循环元素:10000,列表是[0,10,100,1000,10000],满足条件0>=10000-10000,结果为[1,2,3,4,5]
当前循环元素:20000,列表是[0,10,100,1000,10000,20000],0>=20000-10000——False,移除列表中的元素:0,当前列表[10,100,1000,10000,20000];
继续判断:10>=20000-10000——False,移除列表中的元素:10,
当前列表[100,1000,10000,20000];
继续判断:100>=20000-10000——False,移除列表中的元素:100,
当前列表[1000,10000,20000];
继续判断:1000>=20000-10000——False,移除列表中的元素:1000,
当前列表[10000,20000];
继续判断:10000>=20000-10000——True,不再移除,当前列表[10000,20000];
结果为[1,2,3,4,5,2]
当前循环元素:100000,列表是[0,10,100,1000,10000,20000,100000],0>=100000-10000——False,移除列表中的元素:0,当前列表[10,100,1000,10000,20000,100000];
继续判断:10>=100000-10000——False,移除列表中的元素:10,
当前列表[100,1000,10000,20000,100000];
继续判断:100>=100000-10000——False,移除列表中的元素:100,
当前列表[1000,10000,20000,100000];
继续判断:1000>=100000-10000——False,移除列表中的元素:1000,
当前列表[10000,20000,100000];
继续判断:10000>=100000-10000——False,移除列表中的元素:10000,
当前列表[20000,100000];
继续判断:20000>=100000-10000——False,移除列表中的元素:20000,
当前列表[100000];
继续判断:100000>=100000-10000——True,不再移除,当前列表[100000];
结果为[1,2,3,4,5,2,1]
我们可以从上述过程中发现,列表添加元素是在列表的右端,列表移除元素是在列表的左端。
这符合队列的规律(新数据项的添加总发生在一端(通常称为“尾 rear”端) 而现存数据项的移除总发生在另一端(通常称为 “首front”端) )
1.创建一个队列用来模拟过程,创建一个列表用来记录各个元素中满足条件的元素个数(i>=k-10000)
2.从左往右依次使列表的元素从队列的队首(右端)进入,判断队列的尾端(左端)元素是否满足i>=i-10000,如不满足,删除元素,继续判断左端元素,直到满足条件时停止,统计队列中的元素个数。
3.将统计的个数添加到记录列表中。
#作业2:最近的请求次数————优化运行时间
class Queue(list):
def enqueue(self,item):
self.append(item)
def dequeue(self):
return self.pop(0)
def size(self):
return len(self)
def isEmpty(self):
return self==[]
def func(mylist):
q=Queue() #创建一个队列类的实例对象
ls=[] #创建一个列表用来保存计数
for k in mylist:
q.enqueue(k) #元素进队
while q[0] < k-10000: #当左侧元素不满足条件时,一直进行删除操作
q.dequeue()
ls.append(q.size()) #注意:新生成的列表要求与原列表等长,所以放在原列表的循环下
return ls
mylist = eval(input())
print(func(mylist))
运行后,测试样例没有完全通过,发现,我少考虑了一种情况,就是在原列表中,当前循环元素的右侧元素可能与它相等,如果我们直接不判断它的相等右侧元素,那么统计次数就会比真实的少,所以我们不能忽略,那么我们更改一下列表当前循环元素入队的条件:
#作业2:最近的请求次数————优化运行时间
class Queue(list):
def enqueue(self,item):
self.append(item)
def dequeue(self):
return self.pop(0)
def size(self):
return len(self)
def isEmpty(self):
return self==[]
def func(mylist):
q=Queue() #创建一个队列类的实例对象
ls=[] #创建一个列表用来保存计数
for i in range(len(mylist)):
q.enqueue(mylist[i]) #元素进队
count = 0 #引入计数器,对列表中与当前循环元素相同的元素个数进行统计
for j in range(i+1,len(mylist)):
if mylist[j] == mylist[i]:
count+=1
while q[0] < mylist[i]-10000: #当左侧元素不满足条件时,一直进行删除操作
q.dequeue()
ls.append(q.size()+count) #注意:新生成的列表要求与原列表等长,所以放在原列表的循环下
return ls
mylist = eval(input())
print(func(mylist))
啊西,处理了元素重复问题,运行时间又超了…
搜了一下别人的解法,测试用例全部通过了。
人家没有通过定义队列的类来实现队列,可能减少了不少时间复杂度吧,下面是代码:
def func(mylist):
output = []
new_list = [] # 用列表来模拟队列
for i in range(len(mylist)):
new_list.append(mylist[i])
while new_list[-1] - new_list[0] > 10000:
new_list.pop(0)
count = 0
for j in range(i+1, len(mylist)):
if mylist[j] == mylist[i]:
count += 1
else:
break
output.append(len(new_list)+count)
return output
mylist = eval(input())
print(func(mylist))
原文链接:https://blog.csdn.net/Divine0/article/details/104971270
3基数排序(10分)
题目内容:
实现一个基数排序算法,用于10进制的正整数从小到大的排序。
思路是保持10个队列(队列0、队列1…队列9、队列main),开始,所有的数都在main队列,没有排序。
第一趟将所有的数根据其10进制个位(09),放入相应的队列09,全放好后,按照FIFO的顺序,将每个队列的数合并排到main队列。
第二趟再从main队列队首取数,根据其十位的数值,放入相应队列0~9,全放好后,仍然按照FIFO的顺序,将每个队列的数合并排到main队列。
第三趟放百位,再合并;第四趟放千位,再合并。
直到最多的位数放完,合并完,这样main队列里就是排好序的数列了。
输入格式:
一个列表mylist,其中mylist包含一些需要排序的正整数,正整数互不相同且均不超过100000,且个数在1至1000之间。
输出格式:
一个与mylist等长的列表。
输入样例:
[8, 91, 34, 22, 65, 30, 4, 55, 18]
输出样例:
[4, 8, 18, 22, 30, 34, 55, 65, 91]
问题分析:
有一主队列:[8, 91, 34, 22, 65, 30, 4, 55, 18] 要进行排序
第一轮
从左往右将它们从主队列依次出队,根据个位数的数值分配至编号0到9的子队列中:
0 :30
1:91
2:22
3:
4:34 , 4
5:65 ,55
6:
7:
8:8, 18
9:
接下来将这些子队列的元素依次出队,进入主队列:
[30,91,22,34,4,65,55,8,18]
可以看出这些数已经按个位数从大到小的顺序排好了
接着再进行一次分配,这次是根据十位数来分配:
0:4,8
1:18
2:22
3:30,34
4:
5:55
6:65
7:
8:
9:91
接下来将这些子队列的元素依次出队,进入主队列:
[4,8,18,22,30,34,55,65,91]
这时候已经排序完成。(在第二轮排序时已经不用管个位数的排序了(第一轮已经排好),所以目前的主队里,在各个数量级上都按从大到小排好了,我们再把各个数量级进行排序就行了)
如果最高位是3位数或者更多,则继续进行上面直到高位数排完为止
有几位数就需要几轮的排序
1.先找出列表中的最大值
2.确定操作循环次数
n=1
while max(mylist)>10**n:
n+=1 #n已经被修改
for i in range(n): #总共将以下操作进行n次(出主队,进主队)
# your code(出主队,进主队)
那么如何来确定当前提取的数值该进入哪一个子队列呢:
应该是根据该数值在不同数位的基数来进入子队列
for j in range(len(mylist)): #从主队出队次数:队列中原有的元素个数,但不能用队列表示,因为下面的操作改变了队列
num=q_main.dequeue()
radix=int((num/(10**i))%10) #求出num各个数位的基数(技巧),可以自己推一遍
q[radix].enqueue(num) #num根据基数进入相应的子队列
接下来遍历所有子队列,取出子队列中的数值,放进主队列中
#出子队,进主队
for i in range(10):
while not q[i].isEmpty(): #如果当前子队列不空,就一直让元素从子队列出来,然后进入主队列
q_main.enqueue(q[i].dequeue())
下面是完整代码:
#定义队列的类
class Queue(list):
def enqueue(self,item):
self.append(item)
def dequeue(self):
return self.pop(0)
def size(self):
return len(self)
def isEmpty(self):
return self==[]
def func(mylist):
#创建11个队列:
''' #用列表推导式创建10个队列'''
q=[Queue() for i in range(10)]
'''这个操作会生成一个列表,列表中有十个队列,q[0]~q[9]可以访问。
在之前的课中,我们还用过列表推导式x={j:None for j in range(100)}
l=[i for i in range(1000)]来创建字典,列表等方法'''
q_main=Queue() #创建主队列
for num in mylist: #列表中的元素进队
q_main.enqueue(num)
#计算列表中最大元素的位数——也是我们要进行操作的回合数
n=1
while max(mylist)>10**n:
n+=1 #n已经被修改
for i in range(n): #总共将以下操作进行n次(出主队,进主队)
#出主队,进子队
for j in range(len(mylist)): #出队len(mylist)次
num=q_main.dequeue()
radix=int((num/(10**i))%10) #求出num各个数位的基数(技巧),可以自己推一遍
q[radix].enqueue(num) #num根据基数进入相应的子队列
#出子队,进主队
for i in range(10):
while not q[i].isEmpty(): #如果当前子队列不空,就一直让元素从子队列出来,然后进入主队列
q_main.enqueue(q[i].dequeue())
return q_main
mylist = eval(input())
print(func(mylist))
但测试样例3未通过,其他测试样例和时间复杂度正常,不知道为什么(可能以后还会回来再看?先就这样吧)