Bootstrap

每日一题——Python实现PAT乙级1023 组个最小数(举一反三+思想解读+逐步优化)五千字好文

34f0aab6124a400bb321dd38682233c3.jpeg
一个认为一切根源都是“自己不够强”的INTJ

b450501d80dc4e5e820b4403e45bc84a.png个人主页:用哲学编程-CSDN博客
b450501d80dc4e5e820b4403e45bc84a.png专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

 我的写法(刚学Python时)

代码点评

时间复杂度分析

空间复杂度分析

总结

我要更强

优化建议

优化后的代码

时间复杂度和空间复杂度分析

总结

哲学和编程思想

简洁性(Simplicity):

抽象(Abstraction):

效率(Efficiency):

安全性(Security):

迭代与增量开发(Iterative and Incremental Development):

算法和数据结构(Algorithms and Data Structures):

模块化(Modularity):

测试驱动开发(Test-Driven Development, TDD):

举一反三


 

 

题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/type/7?problemSetProblemId=994805298269634560&page=0b1f1de62081e4f399f13cfc0b0887422.png

 我的写法(刚学Python时)

# 读取输入,将输入的字符串按空格分割成列表
times = input().split()

# 初始化索引变量i为0
i = 0

# 循环10次,将列表中的字符串转换为整数
while i < 10:
    times[i] = eval(times[i])  # 将字符串转换为整数
    i += 1

# 初始化一个空列表nums
nums = []

# 初始化索引变量i为0,j为1
i = 0
j = 1

# 循环10次,根据times列表中的数字生成nums列表
while i < 10:
    while j <= times[i]:
        nums.append(i)  # 将当前的数字i添加到nums列表中
        j += 1
    i += 1
    j = 1  # 重置j为1,为下一轮循环做准备

# 对nums列表进行排序
nums.sort()

# 初始化索引变量i为0
i = 0

# 查找nums列表中第一个大于0的元素,并将其移动到列表的开头
while i < len(nums):
    if nums[i] > 0:
        tmp = nums[i]
        nums[1:i+1] = nums[:i]  # 将i之前的元素向后移动一位
        nums[0] = tmp  # 将找到的第一个大于0的元素放到列表的第一个位置
        break
    i += 1

# 遍历nums列表,打印每个元素,不换行
for num in nums:
    print(f"{num}", end='')

# 打印一个换行符,结束输出
print()

 

代码点评

这段代码实现了根据用户输入的10个数字(表示从0到9的每个数字在输出中出现的次数)来生成一个数字序列,并对序列进行排序和调整,确保第一个出现的非零数字位于序列的第一个位置。下面是对代码的点评:

  1. 输入处理:代码首先通过input().split()读取用户输入,并将输入的字符串按空格分割成列表。然后使用eval()函数将列表中的字符串转换为整数。这里使用eval()虽然方便,但存在安全风险,特别是当输入来自不可信源时。建议使用int()函数替代。
  2. 列表生成:代码通过两个嵌套循环生成nums列表,外层循环遍历0到9,内层循环根据times列表中的值重复添加数字。这种方法直观但效率不高,特别是当times中的值较大时。
  3. 排序和调整:代码对nums列表进行排序,然后查找并调整第一个非零元素到列表的开始位置。这部分逻辑清晰,但使用了切片操作来移动元素,这在Python中是比较耗时的操作。
  4. 输出:代码使用for循环遍历nums列表并打印每个元素,最后打印一个换行符。输出部分简洁明了。

时间复杂度分析

  • 输入处理:O(10),因为只处理10个元素。
  • 列表生成:O(n),其中n是所有数字出现的总次数。因为需要遍历每个数字并根据其出现次数生成元素。
  • 排序:使用Python的内置sort()方法,时间复杂度为O(n log n)。
  • 调整元素:O(n),因为需要遍历列表找到第一个非零元素,并移动元素。
  • 输出:O(n),需要遍历整个列表。

总时间复杂度为O(n + n log n + n) = O(n log n),其中n是所有数字出现的总次数。

空间复杂度分析

  • 存储输入:O(10),因为只存储10个元素。
  • 生成列表:O(n),需要存储所有生成的数字。

总空间复杂度为O(n),其中n是所有数字出现的总次数。

总结

这段代码的逻辑清晰,但效率不是最优的,特别是对于大的输入数据。可以通过优化列表生成和元素移动的方式来提高效率。此外,输入处理部分应避免使用eval()函数,以提高代码的安全性。


我要更强

优化建议

  1. 输入处理:使用map()函数将字符串列表直接转换为整数列表,避免使用eval()。
  2. 列表生成:使用列表推导式简化代码,减少循环嵌套。
  3. 排序和调整:避免使用切片操作,改用交换元素的方式来调整列表。
  4. 输出:保持不变。

优化后的代码

 

# 读取输入,将输入的字符串按空格分割成列表,并转换为整数列表
times = list(map(int, input().split()))

# 使用列表推导式生成nums列表
nums = [i for i in range(10) for _ in range(times[i])]

# 对nums列表进行排序
nums.sort()

# 查找并调整第一个非零元素到列表的开始位置
for i in range(len(nums)):
    if nums[i] > 0:
        # 交换元素,将第一个非零元素移动到列表的第一个位置
        nums[0], nums[i] = nums[i], nums[0]
        break

# 遍历nums列表,打印每个元素,不换行
for num in nums:
    print(f"{num}", end='')

# 打印一个换行符,结束输出
print()

时间复杂度和空间复杂度分析

  • 输入处理:O(10),因为只处理10个元素。
  • 列表生成:O(n),其中n是所有数字出现的总次数。使用列表推导式生成列表。
  • 排序:O(n log n),使用Python的内置sort()方法。
  • 调整元素:O(n),因为需要遍历列表找到第一个非零元素,并交换元素。
  • 输出:O(n),需要遍历整个列表。

总时间复杂度为O(n + n log n + n) = O(n log n),其中n是所有数字出现的总次数。

  • 存储输入:O(10),因为只存储10个元素。
  • 生成列表:O(n),需要存储所有生成的数字。

总空间复杂度为O(n),其中n是所有数字出现的总次数。

总结

优化后的代码在逻辑上更加简洁,避免了不必要的循环和复杂的列表操作,提高了代码的效率和可读性。通过使用列表推导式和元素交换,减少了不必要的时间和空间开销。


哲学和编程思想

优化代码时所采用的方法体现了多种哲学和编程思想,具体包括:

  1. 简洁性(Simplicity):

    • 使用列表推导式简化了代码结构,减少了嵌套循环的复杂性。这种简洁性不仅提高了代码的可读性,也使得代码更易于维护和理解。
  2. 抽象(Abstraction):

    • 通过使用Python的内置函数如map()和sort(),代码抽象了底层的实现细节,使得开发者可以专注于问题的逻辑而不是具体的实现方法。
  3. 效率(Efficiency):

    • 优化时间复杂度和空间复杂度的方法体现了对效率的追求。例如,避免使用eval()和切片操作,选择更高效的列表推导式和元素交换,都是为了减少不必要的计算和内存使用。
  4. 安全性(Security):

    • 避免使用eval()函数,因为eval()可能执行任意代码,存在安全风险。这体现了在编程中对安全性的考虑,特别是在处理外部输入时。
  5. 迭代与增量开发(Iterative and Incremental Development):

    • 通过逐步优化代码,每次关注一个小部分,如先优化输入处理,再优化列表生成,最后优化排序和输出,体现了迭代和增量开发的哲学。这种方法有助于逐步改进代码,同时易于测试和调试。
  6. 算法和数据结构(Algorithms and Data Structures):

    • 理解并应用合适的数据结构(如列表)和算法(如排序)是编程中的核心思想。通过选择合适的数据结构和算法,可以显著提高程序的性能。
  7. 模块化(Modularity):

    • 尽管代码本身较短,但通过将不同的功能(如输入处理、列表生成、排序等)分隔开来,体现了模块化的思想。这种模块化的结构有助于代码的组织和维护。
  8. 测试驱动开发(Test-Driven Development, TDD):

  • 在优化过程中,可能需要编写测试用例来验证每个优化步骤是否正确。这种测试先行的方法有助于确保代码的正确性和稳定性。

通过这些哲学和编程思想的应用,优化后的代码不仅在性能上有所提升,而且在可读性、可维护性和安全性方面也有所增强。这些原则和思想是编程和软件开发中的重要组成部分,有助于构建高质量的软件系统。


举一反三

基于上述哲学和编程思想,以下是一些实用的技巧和建议,帮助你在编程时举一反三:

  1. 保持代码简洁:
    • 使用Python的列表推导式、生成器表达式和函数式编程特性(如map()、filter()、reduce())来简化代码。
    • 避免过度复杂的逻辑和嵌套,尽量保持代码的扁平化。
  2. 利用抽象:
    • 使用现有的库和框架来避免重复造轮子。例如,使用NumPy或Pandas处理数据,使用Django或Flask构建Web应用。
    • 创建和使用自己的函数和类来封装复杂的逻辑,提高代码的可重用性。
  3. 优化效率:
    • 了解并应用时间复杂度和空间复杂度的概念,选择最合适的算法和数据结构。
    • 使用性能分析工具(如Python的cProfile)来识别和优化瓶颈。
  4. 注重安全性:
    • 在处理用户输入时,始终进行验证和清理,避免使用eval()等危险函数。
    • 使用参数化查询或ORM(对象关系映射)来防止SQL注入攻击。
  5. 采用迭代和增量开发:
    • 将大问题分解为小问题,逐步解决。每个小问题解决后,进行测试确保其正确性。
    • 使用版本控制系统(如Git)来管理代码的迭代过程。
  6. 掌握算法和数据结构:
    • 学习和练习常见的算法和数据结构,如排序算法、搜索算法、链表、树、图等。
    • 在解决问题时,思考如何利用这些算法和数据结构来提高效率。
  7. 实践模块化:
    • 将代码分解为模块或包,每个模块负责一个明确的功能。
    • 使用接口和抽象类来定义模块之间的交互,提高代码的灵活性和可扩展性。
  8. 采用测试驱动开发:
  • 在编写代码之前,先编写测试用例。这有助于明确需求,并确保代码的正确性。
  • 使用单元测试框架(如Python的unittest或pytest)来编写和运行测试。

通过实践这些技巧,不仅能够提高编程效率和代码质量,还能够培养解决复杂问题的能力。记住,编程是一个不断学习和实践的过程,持续的练习和探索将帮助成为一个更优秀的程序员。


 

 

;