每日一题——Python实现PAT甲级1041 Be Unique(举一反三+思想解读+逐步优化)


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

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

Python-3.12.0文档解读

目录

我的写法

代码点评

时间复杂度分析

空间复杂度分析

总结

我要更强

方法1:一次遍历,使用数组模拟哈希表

方法2:双哈希表优化

方法3:使用有序字典

优化分析

小结

哲学和编程思想

1. 时间和空间权衡(Time-Space Tradeoff)

2. 单一职责原则(Single Responsibility Principle)

3. 最小化遍历次数(Minimizing Passes Over Data)

4. 哈希表和数组的使用(Hash Tables and Arrays)

5. 顺序保持(Order Preservation)

6. 空间优化(Space Optimization)

7. 提前退出(Early Exit)

具体方法中的编程思想应用总结:

举一反三

1. 理解问题和数据特性

2. 选择合适的数据结构

3. 最小化遍历次数

4. 空间优化

5. 单一职责原则

6. 时空权衡

7. 算法思想

举一反三技巧:

技巧1:模式识别

技巧2:优化现有算法

技巧3:保持代码可读性和可维护性

实例应用:

分析问题:

选择数据结构:

设计算法:

优化和测试:


 题目链接


我的写法

 

import sys  # 导入sys模块,用于退出程序# 从标准输入读取一行,并将其转换为整数列表
# 首先分割输入的字符串,使用map函数将每个分割的部分转换为整数,然后再转换为列表
N_nums = list(map(int, input().split()))# 取出列表的第一个元素,作为N值
N = N_nums[0]# 取出列表中除第一个元素外的所有元素,作为新的列表
N_nums = N_nums[1:]# 初始化一个空字典,用于记录每个数字出现的次数
is_unique = {}# 遍历N_nums列表中的每个数字
for num in N_nums:# 检查当前数字是否在字典中if is_unique.get(num, None):# 如果数字已经在字典中,次数加1is_unique[num] += 1else:# 如果数字不在字典中,将其加入字典,并初始化次数为1is_unique[num] = 1# 遍历字典中的每个键值对
for k, v in is_unique.items():# 检查当前键的值是否为1(即该数字只出现了一次)if v == 1:# 如果找到一个只出现了一次的数字,打印该数字,并退出程序print(f"{k}")sys.exit(0)# 如果没有找到只出现一次的数字,打印"None"
print("None")

这段代码的功能是查找并输出一组整数中第一个只出现一次的数字。如果没有找到这样的数字,则输出"None"。以下是对这段代码的点评以及时间复杂度和空间复杂度的分析:

代码点评

  1. 输入处理:
    • N_nums = list(map(int, input().split())) 这一行实现了从标准输入读取一行并将其转换为整数列表。这种处理方式简洁有效。
    • N = N_nums[0] 取出列表的第一个元素作为N值,N_nums = N_nums[1:] 这一行将列表中除第一个元素外的所有元素提取出来。
  2. 统计数字出现次数:
    • 使用一个字典 is_unique 来记录每个数字出现的次数。这种方法非常直观且有效。
    • if is_unique.get(num, None): 这一行可以简化为 if num in is_unique:,这样更清晰一些。
  3. 查找目标数字:
    • 遍历字典 is_unique 来查找第一个只出现一次的数字,并使用 sys.exit(0) 提前退出程序。这种方法在找到目标后立即退出,避免了不必要的遍历。
  4. 输出结果:
  • 如果没有找到只出现一次的数字,程序会输出 "None"。

时间复杂度分析

  1. 输入处理:
    • list(map(int, input().split())):这行代码的时间复杂度为 O(N),其中 N 是输入数字的个数。
  2. 统计数字出现次数:
    • for num in N_nums::这一循环的时间复杂度为 O(N),因为我们需要遍历所有的数字。
    • is_unique.get(num, None) 和 is_unique[num] = 1 等操作在字典中的查找和插入操作平均时间复杂度为 O(1),所以整个循环的时间复杂度为 O(N)。
  3. 查找目标数字:
  • for k, v in is_unique.items()::这一循环的时间复杂度为 O(U),其中 U 是字典中不同数字的个数。由于最多有 N 个不同的数字,最坏情况下 U 为 N,因此最坏情况下这一部分的时间复杂度为 O(N)。

综合起来,整个程序的时间复杂度为 O(N)。

空间复杂度分析

  1. 输入处理:
    • N_nums 列表的空间复杂度为 O(N)。
  2. 统计数字出现次数:
  • is_unique 字典的空间复杂度为 O(U),其中 U 是不同数字的个数。最坏情况下 U 为 N,因此空间复杂度为 O(N)。

综合起来,整个程序的空间复杂度为 O(N)。

总结

这段代码在实现上是高效且易于理解的。时间复杂度和空间复杂度均为 O(N),很适合处理大量数据。在优化方面,可以将 if is_unique.get(num, None): 改为 if num in is_unique:,使代码更加清晰。总体来说,这段代码在性能和可读性上都表现良好。


我要更强

要优化这个问题,主要的思路是尝试减少对输入列表的多次遍历和对数据的额外存储。下面是一些可能的改进方法:

方法1:一次遍历,使用数组模拟哈希表

在已知数据范围的情况下,可以使用数组来替代字典,这样可以减少哈希开销。但是,这需要先假设数字的取值范围是已知的且不大,比如在[0, 100000]范围内。

import sysdef find_first_unique(nums):max_value = 100001  # 假设数字在 [0, 100000] 范围内count = [0] * max_valuefirst_position = [-1] * max_valuefor i, num in enumerate(nums):if count[num] == 0:first_position[num] = icount[num] += 1first_unique_position = len(nums)for num, cnt in enumerate(count):if cnt == 1:first_unique_position = min(first_unique_position, first_position[num])if first_unique_position == len(nums):print("None")else:print(nums[first_unique_position])sys.exit(0)N_nums = list(map(int, input().split()))
N = N_nums[0]
nums = N_nums[1:]
find_first_unique(nums)

方法2:双哈希表优化

使用两个哈希表,一个记录出现次数,另一个记录每个数字的第一次出现位置,最后遍历第一个哈希表找到只出现一次且位置最靠前的数字。

import sysdef find_first_unique(nums):count = {}first_position = {}for i, num in enumerate(nums):if num in count:count[num] += 1else:count[num] = 1first_position[num] = ifirst_unique_position = len(nums)for num, cnt in count.items():if cnt == 1:first_unique_position = min(first_unique_position, first_position[num])if first_unique_position == len(nums):print("None")else:print(nums[first_unique_position])sys.exit(0)N_nums = list(map(int, input().split()))
N = N_nums[0]
nums = N_nums[1:]
find_first_unique(nums)

方法3:使用有序字典

Python的collections.OrderedDict可以记录插入顺序,同时保留哈希表的高效查找性能。

import sys
from collections import OrderedDictdef find_first_unique(nums):count = OrderedDict()for num in nums:if num in count:count[num] += 1else:count[num] = 1for num, cnt in count.items():if cnt == 1:print(num)sys.exit(0)print("None")N_nums = list(map(int, input().split()))
N = N_nums[0]
nums = N_nums[1:]
find_first_unique(nums)

优化分析

  • 时间复杂度:所有方法的时间复杂度均为O(N),因为我们只需要遍历输入列表一次。
  • 空间复杂度:
  • 方法1的空间复杂度为O(M),其中M是数字的范围(即max_value)。
  • 方法2和方法3的空间复杂度为O(N),因为在最坏情况下,我们需要存储所有数字的出现次数和位置。

小结

这些方法在时间复杂度上并没有比原始方法更优,但在某些情况下可以减少空间占用,或者代码更加简洁和清晰。希望这些方法能对问题有所帮助。


哲学和编程思想

在编写和优化算法的过程中,我们不仅要考虑代码的正确性和效率,还会运用到一些计算机科学和编程的哲学思想。这些思想能够帮助我们更好地理解问题、设计解决方案和分析性能。以下是一些相关的哲学和编程思想,以及它们在上述方法中的具体应用。

1. 时间和空间权衡(Time-Space Tradeoff)

这个思想强调在编程中时间复杂度和空间复杂度之间往往存在一种权衡关系。有时为了提高运行速度,我们需要使用更多的空间,反之亦然。

  • 方法1:使用数组模拟哈希表 这里我们用一个固定大小的数组来替代字典,以减少哈希表的开销。虽然这在空间上可能会有些浪费(因为我们假设了一个最大值),但它可以提高访问速度。

2. 单一职责原则(Single Responsibility Principle)

这是编程中的一个重要原则,强调每个模块或函数应该有且只有一个职责。

  • 方法1、方法2、方法3 在这些方法中,我们将统计次数和记录位置的逻辑分离成不同的数据结构(如字典或数组),使得每个数据结构只负责一件事:记录次数或记录第一次出现的位置。这种分离使得代码更清晰、更易于维护。

3. 最小化遍历次数(Minimizing Passes Over Data)

在处理大数据集时,减少对数据的多次遍历可以大大提高算法效率。

  • 所有方法 这些方法都只遍历输入列表一次或两次,避免了多次遍历带来的时间开销。一次遍历统计次数,第二次遍历查找第一个只出现一次的元素。

4. 哈希表和数组的使用(Hash Tables and Arrays)

哈希表和数组是高效的数据结构,常用于快速查找。

  • 方法1、方法2、方法3 在这些方法中,我们使用了哈希表和数组来记录每个数字的出现次数和位置。哈希表提供了常数时间的查找和插入操作,而数组在已知取值范围时可以更高效地模拟哈希表。

5. 顺序保持(Order Preservation)

在某些问题中,保持数据的输入顺序是非常重要的。

  • 方法3:使用有序字典 OrderedDict在插入元素时会保持元素的插入顺序,这对于需要保持顺序的算法非常有用。在这个方法中,我们利用了OrderedDict的顺序保持特性来确保按输入顺序查找第一个只出现一次的元素。

6. 空间优化(Space Optimization)

通过巧妙的设计,可以在不增加时间复杂度的情况下优化空间使用。

  • 方法1:使用固定大小数组 通过使用一个固定大小的数组,我们减少了哈希表的开销。这在数字范围已知且不大的情况下是非常高效的。

7. 提前退出(Early Exit)

当我们已经找到所需结果时,可以提前退出,避免不必要的计算。

  • 所有方法 这些方法在找到第一个只出现一次的数字后都会立即退出程序,避免了继续进行无用的计算。这种提前退出的策略可以在实际应用中显著提高性能。

具体方法中的编程思想应用总结:

  • 方法1:一次遍历,使用数组模拟哈希表
    • 时间和空间权衡:用固定大小的数组来提高访问速度。
    • 最小化遍历次数:只需两次遍历。
    • 空间优化:使用数组代替哈希表。
  • 方法2:双哈希表优化
    • 单一职责原则:分离统计次数和记录位置的逻辑。
    • 最小化遍历次数:只需两次遍历。
    • 哈希表的使用:高效记录和查找。
  • 方法3:使用有序字典
  • 顺序保持:使用OrderedDict保持插入顺序。
  • 最小化遍历次数:只需两次遍历。
  • 哈希表的使用:高效记录和查找。

这些编程和计算机科学的哲学思想在实际编码中帮助设计出高效、可读、可维护的代码。在处理具体问题时,理解和应用这些思想是非常重要的。


举一反三

理解并应用编程和计算机科学的哲学思想能够帮助你在面对不同问题时举一反三,找到高效的解决方案。以下是一些具体的技巧和方法,结合上面的思想,能帮助在编程中更加灵活和高效。

1. 理解问题和数据特性

  • 分析输入输出:仔细阅读并理解问题,明确输入输出要求,并注意数据范围和边界情况。
  • 数据特性:了解数据的特性,比如数据的范围、是否有重复、是否有序等,这些特性可以启发你选择合适的数据结构和算法。

2. 选择合适的数据结构

  • 数组:适用于已知范围且大小固定的数据,访问速度快。
  • 哈希表:适用于需要快速查找和插入的数据,常数时间复杂度。
  • 有序字典/集合:保留数据插入顺序的同时,提供快速查找。

3. 最小化遍历次数

  • 一次遍历完成多任务:尽量在一次遍历中完成多个任务,比如统计和记录位置。
  • 提前退出:找到答案后立即退出,避免不必要的计算。

4. 空间优化

  • 合理分配空间:根据数据范围选择合适的空间,比如用数组替代哈希表。
  • 使用位图/布隆过滤器:在空间紧张的情况下使用位图或布隆过滤器来高效记录数据。

5. 单一职责原则

  • 分而治之:将复杂问题拆分为多个子问题,每个子问题独立解决。
  • 模块化设计:将逻辑功能拆分为不同的模块或函数,每个模块或函数只负责一个任务。

6. 时空权衡

  • 时间优先:在时间要求严格的情况下,可能需要牺牲空间来提高速度。
  • 空间优先:在空间有限的情况下,可能需要采用较慢但空间占用小的算法。

7. 算法思想

  • 贪心算法:每一步选择中都采取当前状态下最优的选择,适用于很多优化问题。
  • 动态规划:将复杂问题拆分为简单子问题,通过缓存子问题的结果来避免重复计算。
  • 分治法:将问题递归地分解为更小的子问题,然后合并解决。
  • 回溯法:通过递归尝试所有可能的解决方案,并在不符合要求时回溯。

举一反三技巧:

技巧1:模式识别

  • 识别常见问题模式:比如“寻找第一个不重复的元素”这类问题可以归为统计和查找问题。
  • 借鉴类似问题的解决方案:尝试记住常见问题的解决方法,并在遇到类似问题时进行适配。

技巧2:优化现有算法

  • 分析瓶颈:找出当前算法的瓶颈,看看是否能用更高效的数据结构或算法替代。
  • 空间换时间:在条件允许的情况下,用额外的空间来换取时间上的性能提升。

技巧3:保持代码可读性和可维护性

  • 注重代码风格:保持代码简洁清晰,注释清楚,便于他人理解和维护。
  • 测试覆盖:编写全面的测试用例,确保代码的正确性和鲁棒性。

实例应用:

假设现在遇到一个问题,需要找到一个字符串中第一个不重复的字符。可以遵循上述技巧进行举一反三:

分析问题:
  • 输入是一个字符串,输出是第一个不重复的字符。
  • 需要统计每个字符的出现次数,并保持字符的顺序。
选择数据结构:
  • 字符频率统计:使用哈希表记录每个字符的出现次数。
  • 顺序保持:使用有序字典或维护两个数组,一个记录字符出现顺序,一个记录字符频率。
设计算法:

from collections import OrderedDictdef first_unique_char(s):count = OrderedDict()for char in s:if char in count:count[char] += 1else:count[char] = 1for char, cnt in count.items():if cnt == 1:return charreturn Nones = input("Enter a string: ")
result = first_unique_char(s)
if result:print(f"The first unique character is: {result}")
else:print("No unique character found.")
优化和测试:
  • 测试不同输入场景(空字符串、全重复字符、有多个不重复字符等)。
  • 分析性能,确保对大字符串也能高效运行。

通过这些技巧和方法,可以在实际编码中灵活运用,解决各种复杂问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/337376.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

写大型C工程makefile构建~

正文 最开始学习linux应用开发编写的时候,估计大部分伙伴们都是在一个目录里面编译整个工程,主要是linux通常没有非常合适的集成开发环境。 以前单目录的方式实在太过捡漏,在linux环境中进行C代码工程开发很多时候需要编写一个相对比较通用的…

业务实战————Uibot6.0 .1多页面商品信息抓取RPA机器人

前言 【案例描述】 鲜果记水果店计划在淘宝电商平台上开设一家新店,小微是该企业运营部分的运营专员,主要负责公司商品上架和管理的工作。 公司计划在开店的新品促销活动中增加水果品类红富士苹果。小微需在商品上架前了解目前平台中销量前列的红富士苹…

预编码算法(个人总结)

引言 预编码算法是现代无线通信系统中的关键技术,特别是在多输入多输出(MIMO)系统中。它们通过在发送端对信号进行处理,减少干扰并提高信道容量。这种技术广泛应用于5G、Wi-Fi和卫星通信系统中。本教程将详细介绍预编码算法的背景…

【技术实操】银河高级服务器操作系统实例分享,数据库日志文件属主不对问题分析

1. 问题现象描述 2023 年 06 月 30 日在迁移数据库过程中,遇到数据库 crash 的缺陷,原因如下:在数据库启动时候生成的一组临时文件中,有 owner 为 root 的文件, 文件权限默认为 640, 当数据库需要使用的时…

基于VGG16使用图像特征进行迁移学习的时装推荐系统

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对…

mac电脑鼠标键盘共享软件:ShareMouse for Mac 激活版

ShareMouse 是一款跨平台的键盘和鼠标共享软件,它允许用户在多台计算机之间共享同一组键盘和鼠标,实现无缝的操作和控制。该软件适用于 Windows 和 macOS 系统,并且支持多种连接方式,包括局域网连接和无线连接。 使用 ShareMouse&…

Blueprints - Collision Presets相关

一些以前的学习笔记归档; 在Static Mesh或SkeletalMesh等的属性中,都有Collision Presets: 其中Oject Type只是一个枚举参数,代表设置该Actor为什么类型,Collision Responses代表该Actor对各种类型的Actor有什么反应&a…

HQChart使用教程100-uniapp如何在vue3运行微信小程序

HQChart使用教程100-uniapp如何在vue3运行微信小程序 症状原因分析解决思路解决步骤1. 修改vender.js2. 修改HQChartControl.js 完整实例HQChart代码地址 症状 HQChart插件在uniappvue3的项目编译成小程序以后, 运行会报错,见下图。 原因分析 查了下…

解决docker容器: bash: ping: command not found, 并制作镜像

一. 出现原因 从 dockerhub 拉下来的镜像都是最轻量级的, 不会安装各种工具, 所以使用 ping, vim 等命令, 会出现 command not found 二. 解决方式 2.1 安装工具包 进入到一个正在运行的容器内部, 执行命令: apt-get update 之后会发现, 容器正在更新软件包, 不过最终会由…

apache大数据各组件部署搭建(超级详细)

apache大数据数仓各组件部署搭建 第一章 环境准备 1. 机器规划 准备3台服务器用于集群部署,系统建议CentOS7+,2核8G内存 172.19.195.228 hadoop101 172.19.195.229 hadoop102 172.19.195.230 hadoop103 [root@hadoop101 ~]# cat /etc/redhat-release CentOS Linux rele…

aws emr启动standalone的flink集群

关键组件 Client,代码由客户端获取并做转换,之后提交给JobMangerJobManager,对作业进行中央调度管理,获取到要执行的作业后,会进一步处理转换,然后分发任务给众多的TaskManager。TaskManager,数…

【设计模式】创建型-建造者模式

前言 在面向对象的软件开发中,构建复杂对象时经常会遇到许多挑战。一种常见的解决方案是使用设计模式,其中建造者模式是一个强大而灵活的选择。本文将深入探讨建造者模式的原理、结构、优点以及如何在实际项目中应用它。 一、复杂的对象 public class…

vue3学习使用笔记

1.学习参考资料 vue3菜鸟教程:https://www.runoob.com/vue3/vue3-tutorial.html 官方网站:https://cn.vuejs.org/ 中文文档: https://cn.vuejs.org/guide/introduction.html Webpack 入门教程:https://www.runoob.com/w3cnote/webpack-tutor…

手机离线翻译哪个好?断网翻译也能超丝滑

有时在异国他乡,面对语言不通的窘境,即便是简单的对话也变得异常困难,真是挑战满满! 然而,能离线翻译的软件让语言障碍不再是问题,不必依赖网络也能轻松进行翻译啦~ 只需下载所需的语言包,选择…

Nginx企业级负载均衡:技术详解系列(14)—— 账户认证功能

你好,我是赵兴晨,97年文科程序员。 你有没有听说过Nginx的账户认证功能?这可不只是一个技术问题,它关系到我们上网时的安全和便利。就像家里需要一把钥匙才能进们一样,Nginx的账户认证功能就是确保有只有授权的人才能…

登录校验及全局异常处理器

登录校验 会话技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束.在一次会话中可以包含多次请求和响应会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话请求间共享数据会话跟踪方案 客户端…

华为 CANN

华为 CANN 1 介绍1.1 概述1.2 CANN 是华为昇腾计算产业的重要一环1.3 昇腾系列处理器1.4 昇腾 AI 产业1.5 从 AI 算法到产品化落地流程1.6 多样性计算架构1.7 人工智能各层级图示1.8 人工智能技术发展历史 2 CANN vs CUDA支持平台优化方向编程接口生态系统与应用性能与功能 3 C…

Qt xml学习之calculator-qml

1.功能说明:制作简易计算器 2.使用技术:qml,scxml 3.项目效果: 4.qml部分: import Calculator 1.0 //需要引用对应类的队友版本 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.4 import QtScxml…

[深度学习]yolov10+bytetrack+pyqt5实现目标追踪

【简介】 利用YOLOv10、ByteTrack和PyQt5实现目标追踪是一个强大的组合,可以为用户提供一个交互式的实时目标追踪界面。以下是一个简化版的实现思路描述: 首先,YOLOv10是一个先进的目标检测算法,能够准确识别视频或图像中的目标…

OC IOS 文件解压缩预览

热很。。热很。。。。夏天的城市只有热浪没有情怀。。。 来吧,come on。。。 引用第三方库: pod SSZipArchive 开发实现: 一、控制器实现 头文件控制器定义: // // ZipRarViewController.h // // Created by carbonzhao on 2…