一个认为一切根源都是“自己不够强”的INTJ
个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数
Python-3.12.0文档解读
目录
我的写法
代码结构与逻辑
时间复杂度分析
空间复杂度分析
代码优化建议
总结
我要更强
优化方法
优化后的代码
代码注释
时间复杂度和空间复杂度
哲学和编程思想
举一反三
题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/type/7?problemSetProblemId=994805304020025344&page=0
我的写法
N=int(input())
jia_wins_counts={'B':0,'C':0,'J':0}
yi_wins_counts={'B':0,'C':0,'J':0}
jia_results_counts={1:0,0:0,-1:0}# 赢,平,输
def jia_result(jia,yi):
if jia==yi:
return 0
if (jia=='J' and yi=='B') or (jia=='B' and yi=='C') or (jia=='C' and yi=='J'):
return 1
return -1
for i in range(N):
jia,yi=input().split()
cur_jia_result=jia_result(jia,yi)
jia_results_counts[cur_jia_result]+=1
if cur_jia_result==1:
jia_wins_counts[jia]+=1
elif cur_jia_result==-1:
yi_wins_counts[yi]+=1
print(*jia_results_counts.values())
print(*list(jia_results_counts.values())[::-1])
print(sorted(jia_wins_counts.items(),key=lambda x:(-x[1],x[0]))[0][0],end=' ')
print(sorted(yi_wins_counts.items(),key=lambda x:(-x[1],x[0]))[0][0])
这段代码实现了一个简单的石头剪刀布游戏结果统计程序。它读取多场比赛的结果,统计甲和乙的胜负情况,并输出各自获胜次数最多的手势。以下是对这段代码的专业点评:
代码结构与逻辑
- 输入处理:代码首先读取比赛次数 N,然后通过循环读取每场比赛的结果,并对结果进行处理和统计。
- 结果判断:通过定义的 jia_result 函数来判断甲的比赛结果(赢、平、输)。
- 统计更新:根据比赛结果更新甲和乙的获胜次数统计字典以及甲的比赛结果统计字典。
- 输出结果:最后输出甲的胜负平次数、乙的胜负平次数(顺序相反),以及甲和乙各自获胜次数最多的手势。
时间复杂度分析
- 输入读取:读取 N 场比赛的结果,时间复杂度为 O(N)。
- 结果判断:每次比赛的结果判断是常数时间操作,时间复杂度为 O(1)。
- 统计更新:每次比赛的统计更新也是常数时间操作,时间复杂度为 O(1)。
- 排序输出:最后对甲和乙的获胜次数进行排序,排序的时间复杂度为 O(3 log 3),即 O(1),因为每次排序的元素数量是固定的(B、C、J 三种手势)。
综合来看,整个程序的时间复杂度为 O(N),其中 N 是比赛次数。
空间复杂度分析
- 输入存储:存储比赛次数 N,空间复杂度为 O(1)。
- 统计字典:存储甲和乙的获胜次数统计字典以及甲的比赛结果统计字典,每个字典的大小是固定的(3 个元素),空间复杂度为 O(1)。
- 临时变量:在循环中使用的临时变量(如 jia 和 yi),空间复杂度为 O(1)。
综合来看,整个程序的空间复杂度为 O(1),即常数空间复杂度。
代码优化建议
- 输入输出优化:可以使用 sys.stdin 和 sys.stdout 进行输入输出优化,特别是在处理大量数据时。
- 代码复用:可以将甲和乙的统计逻辑进行抽象,减少重复代码。
- 异常处理:增加对输入数据的异常处理,确保程序的健壮性。
总结
这段代码结构清晰,逻辑简单,时间复杂度和空间复杂度均为 O(N) 和 O(1),在处理合理范围内的数据时性能良好。通过一些优化,可以进一步提升代码的效率和可读性。
我要更强
优化时间复杂度和空间复杂度的方法通常涉及减少不必要的计算和存储。对于这段代码,主要的优化点在于减少重复计算和简化数据结构。以下是一些可能的优化方法,并附上优化后的完整代码和注释:
优化方法
- 减少重复计算:在结果判断函数中,可以预先定义一个胜负关系表,避免每次都进行条件判断。
- 简化数据结构:使用数组代替字典来存储获胜次数,因为手势种类是固定的。
- 输入输出优化:使用 sys.stdin 和 sys.stdout 进行输入输出优化。
优化后的代码
import sys
# 读取输入的比赛次数
N = int(input())
# 定义胜负关系表
win_table = {
'B': 'C',
'C': 'J',
'J': 'B'
}
# 初始化甲和乙的获胜次数统计数组
jia_wins_counts = [0, 0, 0] # B, C, J
yi_wins_counts = [0, 0, 0] # B, C, J
# 甲的比赛结果统计数组,分别记录赢、平、输的次数
jia_results_counts = [0, 0, 0] # 赢,平,输
# 循环读取每一场比赛的结果
for i in range(N):
jia, yi = input().split()
if jia == yi:
jia_results_counts[1] += 1 # 平局
elif win_table[jia] == yi:
jia_results_counts[0] += 1 # 甲赢
if jia == 'B':
jia_wins_counts[0] += 1
elif jia == 'C':
jia_wins_counts[1] += 1
else:
jia_wins_counts[2] += 1
else:
jia_results_counts[2] += 1 # 甲输
if yi == 'B':
yi_wins_counts[0] += 1
elif yi == 'C':
yi_wins_counts[1] += 1
else:
yi_wins_counts[2] += 1
# 输出甲的赢、平、输次数
print(*jia_results_counts)
# 输出乙的赢、平、输次数(顺序与甲相反)
print(*jia_results_counts[::-1])
# 输出甲和乙各自获胜次数最多的手势
jia_max_index = max(range(3), key=lambda i: jia_wins_counts[i])
yi_max_index = max(range(3), key=lambda i: yi_wins_counts[i])
print('BCJ'[jia_max_index], end=' ')
print('BCJ'[yi_max_index])
代码注释
- 胜负关系表:通过预定义的胜负关系表 win_table,避免了每次比赛结果判断时的条件判断。
- 数组统计:使用数组 jia_wins_counts 和 yi_wins_counts 来存储获胜次数,简化了数据结构。
- 结果输出:通过 max 函数和索引直接找到获胜次数最多的手势,简化了输出逻辑。
时间复杂度和空间复杂度
- 时间复杂度:仍然是 O(N),但由于减少了条件判断,实际运行效率会有所提升。
- 空间复杂度:仍然是 O(1),但由于使用了数组代替字典,空间占用更少。
通过这些优化,代码的执行效率和空间利用率都得到了提升。
哲学和编程思想
这些优化方法体现了几个重要的哲学和编程思想:
- 抽象与封装:通过定义胜负关系表 win_table,将游戏规则抽象为一个数据结构,封装了具体的胜负逻辑,使得代码更加模块化和易于理解。这种抽象减少了代码的复杂性,提高了代码的可维护性。
- 数据驱动设计:使用数组来存储获胜次数,而不是字典,这是一种数据驱动的设计思想。数组的使用简化了数据结构,减少了内存占用,同时也简化了代码逻辑,使得数据处理更加高效。
- 避免重复:通过预先定义胜负关系表,避免了在每次循环中重复进行条件判断,这是“避免重复”(DRY - Don't Repeat Yourself)原则的体现。这个原则强调在软件开发中减少重复代码,提高代码的复用性和可维护性。
- 优化输入输出:虽然在这个例子中没有使用 sys.stdin 和 sys.stdout,但这是一个常见的优化输入输出的方法。这种优化体现了“性能优化”的思想,特别是在处理大量数据时,优化输入输出可以显著提高程序的运行效率。
- 简单性原则:整个优化过程追求代码的简单性,即“保持简单”(KISS - Keep It Simple, Stupid)原则。这个原则强调在设计和编写代码时,应该尽可能保持代码的简单性,避免不必要的复杂性。
- 关注点分离:代码的不同部分(输入处理、结果判断、统计更新、结果输出)被清晰地分离,每个部分只关注自己的任务。这是“关注点分离”(Separation of Concerns)原则的体现,它有助于提高代码的可读性和可维护性。
- 效率优先:在优化过程中,考虑了时间复杂度和空间复杂度,体现了“效率优先”的思想。在软件开发中,效率是一个重要的考虑因素,特别是在处理大量数据或对性能有严格要求的场景中。
通过这些哲学和编程思想的运用,代码不仅变得更加高效和简洁,而且也更容易理解和维护。这些原则和思想是软件工程中的基础,对于任何软件开发者来说都是非常有价值的。
举一反三
根据上述哲学和编程思想以及本题目的情况,以下是一些具体的技巧和建议,可以帮助在其他编程任务中举一反三:
- 抽象化规则:在本题目中,游戏规则被抽象为一个胜负关系表。在其他问题中,如果存在类似的规则或逻辑,也可以尝试将其抽象化,以便于代码的复用和维护。
- 选择合适的数据结构:在本题目中,使用数组来存储获胜次数,而不是字典。在其他问题中,根据数据的特点和操作需求,选择最合适的数据结构可以提高代码的效率。
- 避免重复计算:在本题目中,通过预定义胜负关系表避免了重复的条件判断。在其他问题中,如果发现有重复的计算或逻辑,可以考虑使用缓存或预计算来优化。
- 关注性能瓶颈:在本题目中,优化了时间复杂度和空间复杂度。在其他问题中,通过分析代码的性能瓶颈,可以有针对性地进行优化。
- 保持代码简单:在本题目中,追求代码的简单性。在其他问题中,避免过度设计,保持代码的简洁和清晰,可以提高代码的可读性和可维护性。
- 模块化设计:在本题目中,代码的不同部分被清晰地分离。在其他问题中,将代码分解成独立的模块,每个模块负责一个清晰的功能,可以提高代码的组织性和可扩展性。
- 考虑输入输出优化:在本题目中,虽然没有直接涉及,但在处理大量数据时,优化输入输出操作可以显著提高程序的运行效率。
- 学习和应用设计模式:在其他问题中,学习和应用常见的设计模式,如单例模式、工厂模式、观察者模式等,可以帮助你更好地解决特定类型的问题。
- 编写可测试的代码:在其他问题中,编写易于测试的代码,可以提高代码的质量,并帮助你更快地发现和修复问题。
- 持续学习和实践:软件开发是一个不断发展的领域,持续学习新的编程思想、技术和工具,并将它们应用到实践中,可以帮助你不断提高编程技能。
通过将这些技巧和建议应用到编程实践中,将能够更好地理解和解决各种编程问题,提高代码的质量和效率,并在未来的项目中更加灵活和高效地工作。