【PAT_Python解】1045 快速排序

原题链接:PTA | 程序设计类实验辅助教学平台

Tips:以下Python代码仅个人理解,非最优算法,仅供参考!多学习其他大佬的AC代码!

import sys  
  
# 读取输入  
n = int(input().strip())  # 假设n是列表的长度  
ls = list(map(int, sys.stdin.readline().split()))  
  
# 初始化两个数组,用于存储从左到右的局部最大值和从右到左的局部最小值  
left_max = [0] * n  
right_min = [float('inf')] * n  
  
# 填充left_max数组  
left_max[0] = ls[0]  
for i in range(1, n):  
    left_max[i] = max(left_max[i-1], ls[i])  
  
# 填充right_min数组  
right_min[-1] = ls[-1]  
for i in range(n-2, -1, -1):  
    right_min[i] = min(right_min[i+1], ls[i])  
  
# 找出同时是局部最大值和局部最小值的元素  
res = [ls[i] for i in range(n) if left_max[i] == ls[i] == right_min[i]]  
  
# 输出结果  
print(len(res))  
print(*res)

'''12 / 25分,除测试点0均超时
import sys

n = input()
res = []
ls = list(map(int,sys.stdin.readline().split()))
for i in range(len(ls)):
    if max(ls[:i+1]) == ls[i] and min(ls[i:]) == ls[i]:
        res.append(ls[i])
print(len(res))
print(*res)
'''

我们可以从时间复杂度和算法设计的角度进行详细分析。

时间复杂度

原始代码的时间复杂度
原始代码中,对于列表中的每个元素ls[i],超时代码计算了ls[:i+1]的最大值和ls[i:]的最小值。这意味着对于每个元素,都在进行两个子列表的遍历,其中每个子列表的长度是递增的。虽然每次内部调用的max()min()是O(k)的,其中k是子列表的长度,但总体上的时间复杂度由于这种嵌套性质而接近于O(n^2),因为对于每个元素,都在处理越来越长的子列表。

优化代码的时间复杂度
在优化代码中,我使用了两个额外的数组left_maxright_min来分别存储从左到右的局部最大值和从右到左的局部最小值。这两个数组都是通过一次遍历来填充的,因此整体时间复杂度是O(n)。然后,我通过一次列表推导来找出同时满足条件的元素,这个步骤也是O(n)的。因此,整个算法的时间复杂度是O(n)。

算法设计

原始算法

  • 使用显式的for循环遍历列表。
  • 对于每个元素,计算其左侧所有元素的最大值和右侧所有元素的最小值。
  • 检查当前元素是否同时等于这两个值,如果是,则添加到结果列表中。

这种设计直观但效率低下,因为它对于每个元素都进行了不必要的重复计算。

优化算法

  • 使用动态规划的思想,通过额外的空间(两个数组)来避免重复计算。
  • 分别从左到右和从右到左遍历列表,填充left_maxright_min数组。
  • 通过一次列表推导来找出同时满足条件的元素,并直接生成结果列表。

这种设计利用了问题的空间换时间特性,通过预计算来减少后续操作的计算量,从而大大提高了算法的效率。

总结

在时间复杂度方面,优化代码从O(n^2)提升到了O(n),这是一个显著的改进。在算法设计方面,优化代码通过引入额外的空间来存储中间结果,从而避免了不必要的重复计算,使算法更加高效。这种优化是算法设计中常见的技巧,特别是在处理大数据集时。

动态规划(Dynamic Programming, DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划的思想可以简单地理解为“存储子问题的解以避免重复计算”。

让我们用一个更加生活化的例子来解释动态规划的思想:

想象一下,你正在计划一次长途旅行,需要选择从起点到终点的最佳路线。这条路线上有许多中间站点,每个站点之间都有多种交通方式可以选择(比如步行、骑自行车、开车、乘公交等),而每种交通方式都有其对应的成本(时间、金钱等)。

如果你没有采用动态规划的思想,你可能会尝试所有可能的路线组合,计算每一种的总成本,然后选择成本最低的那个。但是,这样的方法会非常耗时,因为随着站点数量的增加,可能的路线组合数量会呈指数级增长。

而采用动态规划的思想,你会这样做:

  1. 定义子问题:首先,你将整个问题分解成一系列更小的、更容易解决的子问题。在这个例子中,子问题可以是“从起点到站点X的最佳路线是什么?”

  2. 解决子问题并存储结果:然后,你开始解决这些子问题,并且把每个子问题的解存储起来。这样,当你需要解决一个更大的子问题时,你可以直接查看并重用之前已经解决过的更小的子问题的解,而不是重新计算它们。

  3. 利用子问题的解来构建更大问题的解:最后,你利用这些已经存储的子问题的解来构建原问题的解。在这个例子中,你可以通过组合从起点到各个中间站点的最佳路线,来找到从起点到终点的最佳路线。

动态规划的关键在于“重叠子问题”和“最优子结构”这两个概念:

  • 重叠子问题:在解决原问题的过程中,你会多次遇到相同的子问题。动态规划通过存储子问题的解来避免重复计算。

  • 最优子结构:原问题的最优解包含其子问题的最优解。这意味着,如果你能找到所有子问题的最优解,那么你就可以通过组合这些最优解来找到原问题的最优解。

希望这个解释能够帮助你更好地理解动态规划的思想!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

新时代先锋

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值