Python每日一练:小艺读书&醉酒的狱卒&非降序数组(详解快排)

文章讲述了作者在解决非降序数组问题时遇到的困扰,从冒泡排序到快速排序加插入排序的各种尝试,最终发现题目要求并非排序,而是简单的数组合并。此外,还介绍了其他两个编程题目——小艺读书和醉酒的狱卒,分别涉及循环计算和位操作的问题。作者强调了正确理解题目和数据的重要性。
摘要由CSDN通过智能技术生成


前言

今天这个非降序数组,阅读解理小学水平,说起来都是泪啊。我折腾了一天都没搞定,从冒泡写到快速排序。换了几种都还不行,我又给快排加上插入排序。结果还是不能全过!我以为题目有问题,我就用了sort测试,还真能过的!证明我排序代码写得烂…郁闷好半天,我决定去偷看测试数据。才发现这数据本身就是有序的,怪不得难度系数只有中…然后就用一分钟搞定。
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、小艺读书

题目描述:
书是人类进步的阶梯。 小艺每周因为工作的原因会选择性的每天多读几页或者少读几页。 小艺想知道一本n页的书她会在周几读完。

输入描述:
第一行输入n(1<=n<=1000); 第二行输入7个整数,分别表示周一~周日的读书页数p(0<=p<=1000)。(不考虑7个整数都为0的情况)

输出答案:(1-7)

代码如下(示例):

class Solution:
    def __init__(self) -> None:
        pass
    
    def solution(self, n, pages):
        result = None

        # TODO: 请在此编写代码
        pi = 0
        while True:
            n = n-pages[pi%7]
            pi += 1
            if n <= 0:
                result = pi%7
                break
                
        return result

非常之简单,因为前面有个+=1,所以后面的结果不用加1。需要注意的是,有可能一周读不完,要多循环一下,嗯~我还知道只算一周只能过40。

二、醉酒的狱卒

题目描述:
某监狱有一个由n个牢房组成的大厅,每个牢房紧挨着。每个牢房里都有一个囚犯,每个牢房都是锁着的。 一天晚上,狱卒感到无聊,决定玩一个游戏。在第一轮,他喝了一杯威士忌,然后跑下大厅,打开每个牢房的锁。在第二轮比赛中,他喝了一杯威士忌,然后跑下大厅,锁上每隔一个的牢房的锁(牢房2、4、6…)。在第三轮比赛中,他喝了一杯威士忌,然后跑下大厅。他每隔三个牢房(第3、6、9号牢房)就去一次。如果牢房被锁上了,他就把它打开;如果牢房门打开了,他就锁上牢房。他重复n轮,喝最后一杯,然后昏倒。 一些囚犯(可能为零号)意识到他们的牢房被解锁且狱卒丧失了行动能力。他们就可以立即逃跑。现在根据牢房数量,确定有多少囚犯越狱。

输入描述:
第一行输入包含一个正整数t。表示有t行数据,下面每一行都包含一个介于5和100之间(含5和100)的整数,即轮数n

输出描述:
对于每一行,必须打印出监狱有n个牢房时越狱的囚犯人数

代码如下(示例):

class Solution:
    def __init__(self) -> None:
        pass
    
    def solution(self, t, vector):
        result = []

        # TODO: 请在此编写代码
        for v in vector:
            v = int(v)
            rooms = []
            rooms = [False for _ in range(v+1)]
            for N in range(1, v+1):
                for i in range(1, v+1):
                    if i*N <= v:
                        rooms[i*N] = not rooms[i*N]
            result.append(sum(rooms))
        result = [str(x) for x in result]
        return result

虽然描述很长,但还真不难,就是描述中有个地方误导人,说有0号牢房,实际上是没有的!外循环是不同的牢房数、内循环是不同的波次。其实是同一个数,先定义了一个全False的列表,然后根据牢房号不停的翻转状态即可。最后用了sum统计True值的个数,就是可以逃跑的牢房数了。

三、非降序数组

这题太误导人了,我怎么看都是要求排序!所以我优化又优化了几波排序算法,采用了快速排序加尾数插入排序的办法。可惜也不能100分。虽然这测试数据有确实过份,长的一个列表约有十万个数。
不过写都写出来了,就在这记录一下,其实也挺不容易的了!

代码如下(不能全过的排序法示例):

def qmid(arr, left, right):
    # 取左、中、右的中值作为基数, 并对此三数排序
    center = (left+right)//2
    if arr[center] < arr[left]:
        arr[center], arr[left] = arr[left], arr[center]
    if arr[right] < arr[left]:
        arr[right], arr[left] = arr[left], arr[right]
    if arr[right] < arr[center]:
        arr[right], arr[center] = arr[center], arr[right]
    # 将基数放在right-1位置
    arr[right-1], arr[center] = arr[center], arr[right-1]
    pivot = arr[right-1]
    return pivot

def insert_sort(arr):

    n = arr.__len__()
    i, j = 1, 0
    while i < n:
        curr = arr[i]  # 待排的数
        j = i - 1
        while j >= 0 and curr < arr[j]:
            arr[j+1] = arr[j]    #以前面的比较
            j -= 1
        arr[j+1] = curr      #插入暂时正确的位置
        i += 1

def qsort(arr, left, right):
    if left+10 <= right:
        pivot = qmid(arr, left, right)
        i, j = left-1, right-1-1
        while True:
            while arr[i] < pivot:
                i += 1
            while arr[j] > pivot:
                j -= 1
            if i < j:
                arr[i], arr[j] = arr[j], arr[i]
            else:
                break
        arr[i], arr[right-1] = arr[right-1], arr[i]
        qsort(arr, left, i-1)
        qsort(arr, i+1, right)
    else:
        insert_sort(arr)

qmid函数是用来计算快排的基准数的,很多人喜欢取值第一个或最后一个,对于无序列表(数组)来说确实也没大问题,但很明显如果有序的数组就会让复杂度变成最差的n^2情况,所以这里采用了首尾中间三个数来比较取值,并顺手把这三个数排了序。尽量争取做到左右各半这种最好的情况。
第二个insert_sort函数是插入排序,它的作用是在快排分割成小于10个数的列表时,直接用插入排序来干了,对于基本有序的一个数组,插入排序的速度是很快的。其基本思想是:从第1个开始与它前面的数比较,小的就和前面的交换,大了就下一个,如此循环一次完成。因为经过前面的快排,数组是基本有序的,所以很快。

第三个函数就是快速排序的主体了,它其实了是冒泡排序的改良。基本思想是选取一个数作为中间值pivot,把比pivot小的放左边,比pivot大的放右边。然后把数组以pivot为界分成左右两半,递归再进行。这应该是已知的公认最快排序方法了。可惜水平不够,写出的过不了100分,有机会应该学习一下自带的排序是怎么写的。

好了,最后我又去参考了别人的思路,才发现这题压根不是让人重排序,给出的数组本身就是有序的,只要合并就行了。那么我们要考虑的就是二个数组哪个小的放前面,大的放后面的事。这就简单了,一个个比较好了:以下是100分代码,很简单

class Solution:
    def __init__(self) -> None:
        pass
    
    def solution(self, n, m, num1, num2):
        result = []

        # TODO: 请在此编写代码
        i, j = 0, 0
        while i<n and j<m:
            if num1[i] < num2[j]:
                result.append(num1[i])
                i += 1
            else:
                result.append(num2[j])
                j += 1
        if i<n:
            result += num1[i:]
        if j<m:
            result += num2[j:]
        result = [str(x) for x in result]
        return result

思路就是逐个比较前面的元素,小的先放入result,再比较下一个,当比较完一个数组后,剩余的直接全加入就行了。


总结

其实最后这题还是挺有意思的,我把最大的那个测试用例给记下来了,准备再想想怎么用排序的办法过关,明明系统自带的sort函数能过的嘛~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无证的攻城狮

如本文对您有用,大爷给打个赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值