一个认为一切根源都是"自己不够强"的INTJ
个人主页:用哲学编程-CSDN博客
专栏:每日一题------举一反三
Python编程学习
Python内置函数
目录
题目链接:https://www.lanqiao.cn/problems/17056/learning/?page=1\&first_category_id=1
我的写法
python
import os
import sys
N=int(input())
nums=set(map(int,input().split()))
print(*sorted([i for i in range(N+1) if i not in nums]))
这段代码的主要功能是读取用户输入的一组整数,然后找出并打印出从1到N的所有缺失的整数。下面是对这段代码的分析:
- 输入处理:
- N=int(input()):读取用户输入的第一个整数,表示序列的长度。
- nums=set(map(int,input().split())):读取用户输入的第二行,将其分割成整数列表,并通过set转换为集合。这样做的好处是集合的成员检查操作(in)的时间复杂度为O(1),相比于列表的O(n)要快很多。
- 缺失数字的查找:
- [i for i in range(N+1) if i not in nums]:这是一个列表推导式,用于生成从0到N(包括N)的所有整数中不在nums集合中的整数。这里使用了if i not in nums来检查每个整数是否在集合中,由于集合的查找操作是常数时间,这一步的时间复杂度是O(N)。
- 输出:
- print(*sorted([i for i in range(N+1) if i not in nums])):首先对列表推导式生成的列表进行排序,然后使用*操作符解包列表并打印出所有元素。排序的时间复杂度是O(N log N),但由于这里N的最大值是输入的N+1,所以整体的时间复杂度可以认为是O(N log N)。
时间复杂度:
- 输入处理:O(N)
- 缺失数字查找:O(N)
- 排序:O(N log N)
- 总时间复杂度:O(N + N log N),通常可以简化为O(N log N)。
空间复杂度:
- 存储输入的整数集合:O(N)
- 存储缺失的整数列表:O(N)
- 总空间复杂度:O(N)
这段代码在处理大数据时效率较高,尤其是利用了集合来优化查找操作。不过,如果N非常大,排序操作可能会成为性能瓶颈。在实际应用中,可以根据具体情况考虑是否需要优化排序步骤。
我要更强
为了优化时间复杂度和空间复杂度,我们可以避免排序操作,并尽可能减少不必要的集合操作。以下是一种优化方法,使用位图(bitmap)来表示数字的存在状态,这样可以以O(1)的时间复杂度检查数字是否存在,并且空间复杂度为O(N)。
python
import os
import sys
# 读取输入
N = int(input())
nums = list(map(int, input().split()))
# 初始化位图,所有位都设置为0
bitmap = [0] * (N + 1)
# 将输入的数字在位图中对应的位设置为1
for num in nums:
if num <= N: # 确保数字不超过N
bitmap[num] = 1
# 遍历位图,打印出所有值为0的索引,即缺失的数字
for i in range(1, N + 1):
if bitmap[i] == 0:
print(i, end=' ')
# 注意:这种方法假设输入的数字都是正整数且不超过N
时间复杂度分析:
- 输入处理:O(N)
- 设置位图:O(N)
- 打印缺失数字:O(N)
- 总时间复杂度:O(N)
空间复杂度分析:
- 位图:O(N)
- 总空间复杂度:O(N)
这种方法在时间和空间上都比原方法更高效,因为它避免了排序操作,并且使用位图来表示数字的存在状态,从而减少了空间的使用。这种方法的一个前提是输入的数字都是正整数且不超过N,如果输入可能包含负数或超过N的数字,这种方法需要进一步的修改。
哲学和编程思想
上述优化方法体现了以下哲学和编程思想:
- 抽象化(Abstraction): 使用位图(bitmap)是一种抽象化的表现,它将数字的存在状态抽象为位的集合。这种抽象化简化了问题的表示和处理,使得我们可以用更少的内存和更快的速度来处理问题。
- 空间与时间的权衡(Space-Time Tradeoff): 原方法中使用了集合来存储数字,这虽然加快了查找速度(O(1)),但增加了空间的使用。优化方法中使用位图,虽然空间复杂度仍然是O(N),但位图的空间效率更高,因为它只使用一个位来表示一个数字的存在状态,而不是整个整数。这种权衡是编程中常见的,通常需要在空间和时间效率之间做出选择。
- 算法优化(Algorithm Optimization): 优化方法避免了排序操作,这是算法优化的一个例子。通过选择合适的算法和数据结构,我们可以显著提高程序的效率。在这个例子中,使用位图代替排序,使得时间复杂度从O(N log N)降低到O(N)。
- 数据结构的选择(Data Structure Selection): 选择合适的数据结构是解决问题的关键。原方法中使用了集合,而优化方法中使用了位图。这两种数据结构都适合处理这个问题,但位图在空间效率上更优。这体现了在编程中选择合适数据结构的重要性。
- 假设与约束(Assumptions and Constraints): 优化方法假设输入的数字都是正整数且不超过N。这种假设简化了问题,但同时也限制了方法的适用范围。在实际编程中,我们需要明确问题的假设和约束,以确保解决方案的正确性和有效性。
- 迭代与增量开发(Iterative and Incremental Development): 在编程实践中,我们通常不会一次性找到最佳解决方案。原方法是一个起点,通过分析其性能,我们可以逐步优化,最终得到更高效的解决方案。这种迭代和增量开发的方法是软件工程中的一个重要思想。
通过这些哲学和编程思想的应用,可以更有效地解决问题,提高代码的质量和性能。
举一反三
基于上述哲学和编程思想,以下是一些技巧和策略,可以帮助在面对类似问题时举一反三:
- 理解问题的本质: 在开始解决问题之前,深入理解问题的本质和需求。例如,在这个问题中,我们需要找出缺失的数字,这意味着我们需要一个能够快速检查数字是否存在的方法。
- 选择合适的数据结构: 根据问题的需求选择最合适的数据结构。例如,位图在表示和检查数字存在性方面非常高效。在其他问题中,可能需要使用列表、集合、字典、树或图等。
- 优化算法: 分析算法的瓶颈,并寻找可能的优化方法。例如,避免不必要的排序操作,或者使用更高效的查找算法。
- 空间与时间的权衡: 在设计解决方案时,考虑空间和时间的权衡。有时候,增加一些额外的空间可以显著减少时间复杂度,反之亦然。
- 利用抽象化: 将复杂的问题抽象为更简单的模型。例如,将数字的存在状态抽象为位图,这样可以简化问题的表示和处理。
- 迭代和增量开发: 不要期望一次性找到完美的解决方案。通过迭代和增量开发,逐步改进你的解决方案。
- 考虑假设和约束: 明确问题的假设和约束,这有助于你设计出既高效又正确的解决方案。例如,如果输入的数字可能包含负数或超过N的数字,你需要相应地调整你的方法。
- 学习和应用算法知识: 掌握常见的算法和数据结构,这可以帮助你在面对新问题时快速找到合适的解决方案。
- 代码复用和模块化: 尽可能复用已有的代码和模块,这可以提高开发效率并减少错误。
- 测试和验证: 在实现解决方案后,进行彻底的测试和验证,确保它满足问题的所有需求。
通过应用这些技巧和策略,可以在面对各种编程问题时更加灵活和高效。记住,编程不仅仅是写代码,更是一种解决问题的艺术。