在数组中找重复数、只出现一次的数或丢失数的题目(Leetcode题解-Python语言)

在一维数组中的考察中,最常见的就是找出数组中的重复数、只出现一次的数或者丢失(消失)数等等。

一般来说,首先想到的就是用哈希表集合)来记录出现过的数,基本所有的题都可以用集合来做,而技巧性在于有时可以把原数组自身作为哈希表
其次就是位运算,原理是相同的数做异或运算 ^ 会得到0,而一个数与0做异或会得到这个数本身
最后,在排好序或者对空间要求为O(1)但又不能修改原数组的情况下,二分查找也是一种方法。

136. 只出现一次的数字(找出一个只出现一次的数字

class Solution:def singleNumber(self, nums: List[int]) -> int:ans = set()for num in nums:if num in ans:ans.remove(num)else:ans.add(num)return ans.pop()

虽然可以用集合解决,但是此题最优的做法是位运算,数组里面所有相同的数异或会得到0,而那个只出现一次的数再与0做异或,直接得到结果本身,代码如下:

class Solution:def singleNumber(self, nums: List[int]) -> int:ans = nums[0]for i in range(1, len(nums)):ans = ans ^ nums[i]return ans

217. 存在重复元素(是否存在重复元素

class Solution:def containsDuplicate(self, nums: List[int]) -> bool:temp = set()for num in nums:if num in temp:return Trueelse:temp.add(num)return False

剑指 Offer 03. 数组中重复的数字(找出一个重复元素

class Solution:def findRepeatNumber(self, nums: List[int]) -> int:temp = set()for num in nums:if num in temp:return numelse:temp.add(num)return -1

用集合轻松解决,但是可以不使用额外的集合,而是将原数组本身当作集合,通过交换使得数组的值与索引(下标)一一对应,若出现两个值对应同一个索引,即为重复的元素。

在这里插入图片描述

class Solution:def findRepeatNumber(self, nums: [int]) -> int:i = 0while i < len(nums):if nums[i] == i:  # 值与索引已经相同,跳过i += 1continueif nums[i] == nums[nums[i]]:   # 值与值所指向下标的值一样,说明是重复数return nums[i]nums[nums[i]], nums[i] = nums[i], nums[nums[i]]return -1

注意: Python 中, a, b = c, d操作的原理是先暂存元组 (c,d) ,然后 “按左右顺序” 赋值给 a 和 b 。因此,若写为 nums[i], nums[nums[i]] = nums[nums[i]], nums[i],则 nums[i] 会先被赋值,之后 nums[nums[i]] 指向的元素则会出错。

260. 只出现一次的数字 III(剑指 Offer 56 - I. 数组中数字出现的次数)(找出两个只出现一次的数字

回想到,对所有数字进行异或就可以得到结果,本题中其余数字也是出现两次,区别在于有两个数字只出现一次。在这里,我们会希望这两个数字分别出现在两组中,对这两组都进行异或,这样就能得到答案了。怎么做呢?线索在于,对所有数字进行异或后的结果,考虑其每一位取值的意义,如果为0,说明这两个数字的这一位相同,如果为1则不相同。

找到第一位为1的,说明这两个数在这一位上一个为1、一个为0。以此我们可以把数组分为两部分,这两个数各自存在于这两部分中,划分的依据就是这一位的取值。至于其他出现两次的数,在分组时相同的数一定在同一组,因此对这两组都进行全部异或,出现两次的数会抵消,最后剩下这两个数字。

class Solution:def singleNumber(self, nums: List[int]) -> List[int]:# 全部异或ret = reduce(lambda x, y: x ^ y, nums) # reduce(二元函数, 可迭代对象)h = 1while h & ret == 0:  # 从右边开始找第一位为1的(两个数不同的位)h <<= 1a, b = 0, 0# 分别异或for n in nums:if n & h:  # n 在这一位是 1,一个组a ^= nelse:      # n 在这一位是 0,另一个组b ^= nreturn [a, b]

137. 只出现一次的数字 II(剑指 Offer 56 - II. 数组中数字出现的次数 II)(剑指 Offer II 004. 只出现一次的数字 )(找出一个只出现一次的数字,然而其他数字都出现三次

class Solution:def singleNumber(self, nums: List[int]) -> int:counter = collections.Counter(nums)ans = [num for num, val in counter.items() if val == 1]return ans[0]

这一题用集合的话,还不能简单地出现过就 pop,没出现过就 push,因为重复数字是出现三次的,所以应该用 Counter 来解决,更加优化的思路是借鉴数字电路的:

class Solution:def singleNumber(self, nums: List[int]) -> int:a = 0b = 0for i in range(len(nums)):b = (b ^ nums[i]) & ~aa = (a ^ nums[i]) & ~breturn b

思路是设置一个状态机,有 a、b 两个记录器:第一次碰到数字 x 时,记录器 b 记录下来,记录器 a 为 0;第二次碰到数字 x 时,记录器 a 记录下来,记录器 b 为 0;第三次碰到数字 x 时,两个记录器都为 0。这样遍历所有数字之后,出现三次的为 0,出现一次的就存放在记录器 b 中。

实现记录器 b 的方法:第一次碰到 x 记录,第二次碰到 x 变 0,实际上就是异或 b = b ^ x,但是第三次碰到 x 时 b 还是0,区别就只在于记录器 a 的值为 x ,而第一二次时 a 都为0,因此是 b = (b ^ x) & ~a ,对于记录器 a 同理。

268. 丢失的数字(剑指 Offer 53 - II. 0~n-1中缺失的数字)(找出 0 - n 范围中没出现的那一个数

class Solution:def missingNumber(self, nums: List[int]) -> int:n = len(nums)ans = 0for i in range(n):ans = ans ^ nums[i] ^ ians ^= (i + 1)  # 正常的数组,包括 nreturn ans

方法一:正常的数组求和减去缺失的数组求和,差值就是缺失的数;

方法二:正常的数组与缺失的数组做异或,相同的数会异或为0,剩下的就是缺失的数。

448. 找到所有数组中消失的数字(找出多个 1 - n 范围中没出现的数

class Solution:def findDisappearedNumbers(self, nums: List[int]) -> List[int]:n = len(nums)for num in nums:x = (num - 1) % n  # 由于是表示下标,所以 num - 1nums[x] += n       # 加上 n 不会改变其对 n 取余数的结果ans = [i + 1 for i, num in enumerate(nums) if num <= n]  # 没有被加上 n 的下标就是数组里没有的return ans

由于是多个数没出现,所以不能简单地用异或解决,用集合固然可以做,但是更优化地是把原数组本身作为集合,利用值与索引之间的映射关系来找出目标数。此题中,我们把数组中出现了的值对应的下标都加上 n,则没有被加上 n 的下标就是数组里没有的。

442. 数组中重复的数据(找出多个 1 - n 范围中重复出现的数

class Solution:def findDuplicates(self, nums: List[int]) -> List[int]:n = len(nums)for num in nums:x = (num - 1) % n  # 由于是表示下标,所以 num - 1nums[x] += n       # 加上 n 不会改变其对 n 取余数的结果ans = [i + 1 for i, num in enumerate(nums) if num > n * 2]  # 被加上2次 n 的下标就是数组里重复的数return ans

与上一题同理,利用值与索引的对应关系,找出在数组中出现两次的值(其对应下标的值被两次加上了 n)。

287. 寻找重复数(找出唯一的重复出现的数

class Solution:def findDuplicate(self, nums: List[int]) -> int:left = 1right = len(nums) - 1while left < right:mid = left + (right - left) // 2 cnt = 0 # 记录小于等于mid的元素个数for num in nums:if num <= mid:cnt += 1if cnt > mid:right = midelse:left = mid + 1return left

这题比较特别,规定了不能修改数组 nums 且只用常量级 O(1) 的额外空间。给定一个包含 n + 1 个整数的数组,其数字都在 1 到 n 之间,只有一个数字是重复的。因此,对于某个数字 x 来说,正常来说小于等于 x 的数字应该有 x 个,例如有1、2、3、4共4个数字小于等于4,如果大于4了,则说明1、2、3、4其中有一个数字重复了,所以右边界左移,反之左边界右移。此为二分数值型

540. 有序数组中的单一元素(剑指 Offer II 070. 排序数组中只出现一次的数字)(找出有序数组的唯一不重复的数

class Solution:def singleNonDuplicate(self, nums: List[int]) -> int:left = 0right = len(nums) - 1while left < right:mid = left + (right - left) // 2if mid % 2 == 1:   # 只考虑偶数下标mid -= 1if nums[mid] == nums[mid + 1]:  # 如果它和下一个数相同,说明还正常,单一元素在右边区间left = mid + 2else:right = midreturn nums[left]

这题用集合可以做到 O(n) 的时间,但是用二分可以做到 O(logn)。注意到,由于数组中只有一个不重复的数,所以总长度一定是奇数,而首尾下标都为偶数。又因为数组是有序的,所以重复数都是两两一起出现,且正常的情况都是(偶数索引,奇数索引),只有当出现那一个不重复的数(偶数索引),索引才会变成(奇数,偶数)。所以用二分索引法找到每个偶数下标,如果它和下一个数相同,则说明排序还是正常的,即单一元素在右边区间;否则,则说明排序已经不正常,单一元素在左边区间。

41. 缺失的第一个正数(找出数组中没有出现的最小的正整数

class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n = len(nums)for i in range(n):if nums[i] <= 0:nums[i] = n + 1for i in range(n):num = abs(nums[i])if num <= n:nums[num - 1] = -abs(nums[num - 1])for i in range(n):if nums[i] > 0:return i + 1return n + 1

用集合可以做,但是题目要求时间复杂度为 O(n) 并且只使用常数级别额外空间。实际上,对于一个长度为 N 的数组,其中没有出现的最小正整数只能在 [1, N+1] 中。这是因为如果 [1, N] 都出现了,那么答案是 N+1,否则答案是 [1, N] 中没有出现的最小正整数。所以我们的思路就是:不考虑负数,将它们设置为 n + 1;对于在 [1, N] 中的数(此时之前的负数为 n + 1,不会被考虑),将其值对应下标的数变为负(作为已经出现过了的标志);最后找出第一个不是负数的,其对应下标就是没出现的,即为答案。若所有都出现过,答案则为 n + 1。

在这里插入图片描述

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

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

相关文章

Confluent官博:Kafka最牛队列,性能15倍于RabbitMQ!

“容器、Kubernetes、DevOps、微服务、云原生&#xff0c;这些技术名词的频繁出现&#xff0c;预兆着新的互联网技术时代的到来&#xff0c;大数据高并发将不再遥远&#xff0c;而是大部分项目都必须面对的&#xff0c;消息队列则是核心利器&#xff01;成熟的消息队列产品很多…

leetcode503. 下一个更大元素 II

一:题目 二:上码 class Solution { public:/**思路: 1.将两个nums拼接到一块这里拼接到一块,当我们最后的元素找不到比其大的时候 就会开始从头开始这样的话就可以继续进行 入栈 或者出栈的操作入栈就是比我栈顶小的元素&#xff0c;出栈的话 那就是 找到了比其大的元素了…

跟我一起学.NetCore之中间件(Middleware)应用和自定义

前言Asp.NetCore中的请求管道是通过一系列的中间件组成的&#xff0c;使得请求会根据需求进行对应的过滤和加工处理。在平时开发中会时常引用别人定义好的中间件&#xff0c;只需简单进行app.Usexxx就能完成中间件的注册&#xff0c;但是对于一些定制化需求还得自己进行处理和封…

leetcode42. 接雨水

一:题目 二:上码 // class Solution { // public: // /**超时 // 思路: // 1.我们按列来计算 这就是表明的是 我们求取接雨水 向上的高度就是雨水量 // 但是这里的话我们的需要对雨水的高度 来进行判定 // 2.那么如何判定…

Magicodes.IE之导入导出筛选器

总体设计Magicodes.IE是一个导入导出通用库&#xff0c;支持Dto导入导出以及动态导出&#xff0c;支持Excel、Word、Pdf、Csv和Html。在本篇教程&#xff0c;笔者将讲述如何使用Magicodes.IE的导入导出筛选器。在开始之前&#xff0c;我们需要先了解Magicodes.IE目前支持的筛选…

谈了千百遍的缓存数据的一致性问题

“灵魂拷问保证缓存和数据库的一致性很简单吗&#xff1f;有哪些方式能保证缓存和数据库的一致性呢&#xff1f;如果发生了缓存和数据库数据不一致的情况怎么办呢&#xff1f;在上篇文章我们介绍了缓存的定义分类以及优缺点等&#xff0c;如果还没看的同学可以移步这里听说你会…

BS作业 基于springboot + Thymeleaf +mybatis 实现的书城管理系统

一:项目背景 项目描述 一个基本功能较为完整的后台管理项目。项目主要功能有&#xff1a;登录验证&#xff0c;登录功能还加入了随机验证码的验证&#xff1b; 用户注册&#xff0c;注册中密码基于srping 安全框架提供的加密(自动加盐)的密码储存方式&#xff0c;对注册重名进…

Istio Pilot 源码分析(二)

张海东&#xff0c; ‍多点生活&#xff08;成都&#xff09;云原生开发工程师。本篇主要介绍 Pilot 源码中的 ServiceEntryStore 及其推送 xDS 的流程。本文为 Istio Pilot 源码分析系列的第二篇文章。Istio Pilot 源码分析&#xff08;一&#xff09;了解了 Pilot 源码的基本…

Pytorch中的 torch.Tensor() 和 torch.tensor() 的区别

直接在搜索引擎里进行搜索&#xff0c;可以看到官方文档中两者对应的页面&#xff1a; 分别点击进去&#xff0c;第一个链接解释了什么是 torch.Tensor&#xff1a; torch.Tensor 是一个包含单一数据类型元素的多维矩阵&#xff08;数组&#xff09;。 正因为 torch.Tensor 只包…

leetcote34. 在排序数组中查找元素的第一个和最后一个位置

一:题目 二&#xff1a;上码&#xff08;暴力二分&#xff09; // class Solution { // public: // /** // 思路:1.首先这是一个升序的 那么相同的一定是会相连的// */// vector<int> searchRange(vector<int>& nums, int target) {// …

Git 图形化操作之合并提交记录

Git 图形化操作之合并提交记录独立观察员 2020 年 9 月 24 日目录1、显示日志2、合并提交记录3、推送合并的提交前言&#xff1a;当我们使用 Git 时&#xff0c;有时会遇到刚提交推送完一次修改&#xff0c;发现漏了该某处&#xff0c;只好又提交推送一次&#xff0c;这样在提交…

Pytorch中的 torch.as_tensor() 和 torch.from_numpy() 的区别

之前我写过一篇文章&#xff0c;比较了 torch.Tensor() 和 torch.tensor() 的区别&#xff0c;而这两者都是深拷贝的方法&#xff0c;返回张量的同时&#xff0c;会在内存中创建一个额外的数据副本&#xff0c;与原数据不共享内存&#xff0c;所以不受原数据改变的影响。 这里…

chrome禁止三方cookie,网站登录不了怎么办

背景新版chrome(80)浏览器默认屏蔽所有三方cookie已经不是什么新闻了&#xff0c;具体原因这里不去深究&#xff0c;有大量相关文章介绍&#xff0c;由于目前许多网站都依赖三方cookie&#xff0c;因此该特性的推出还是造成了一些的影响&#xff0c;比如收集用户信息的广告商&a…

leetcode69. x 的平方根

一:题目 二:上码 class Solution { public:/**思路:1.因为我们的 ans的平方 < x 那么我们就可以用二分法来做 不断缩小左右范围来确定 ans**/int mySqrt(int x) {int left 0; int right x;int ans 0;while (left < right) {long mid (right-left)/2 left;if (mid*…

初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob储存

点击上方蓝字"小黑在哪里"关注我吧聚合根仓储领域服务BLOB储存应用服务单元测试模块引用前言在前两节中介绍了ABP模块开发的基本步骤&#xff0c;试着实现了一个简单的文件管理模块&#xff1b;功能很简单&#xff0c;就是基于本地文件系统来完成文件的读写操作&…

leetcode367. 有效的完全平方数

一:题目 二:上码 class Solution { public:/**完全平方数:若一个数能表示成某个整数的平方的形式&#xff0c;则称这个数为完全平方数思路:1.我们将num先折半,因为它是某个整数的平方&#xff0c;而这个数的范围肯定不会超过num的一半2.那么这就相当于在[left,num/2]中查找某个…

跟我一起学.NetCore之文件系统应用及核心浅析

前言在开发过程中&#xff0c;肯定避免不了读取文件操作&#xff0c;比如读取配置文件、上传和下载文件、Web中html、js、css、图片等静态资源的访问&#xff1b;在配置文件读取章节中有说到&#xff0c;针对不同配置源数据读取由对应的IConfigurationProvider进行读取&#xf…

深度学习入门笔记(1)——导论部分

此笔记来源于 Sebastian Raschka 的 Introduction to Deep Learning 系列课程。 首先介绍的是传统的编程范式&#xff0c;假设我们想实现垃圾邮件识别的功能&#xff0c;传统的方法就是由程序员来找出垃圾邮件的规则并对其进行编程&#xff0c;得到一个垃圾邮件识别的程序。 机…

深度学习入门笔记(2)—— 感知器

最经典的神经元模型&#xff0c;从左到右依次是&#xff1a;输入、权重、加权和、阈值、输出。加权和又叫做 Net Input&#xff0c;符号为 z&#xff0c;当 z 的值大于阈值时输出 1&#xff0c;小于阈值时输出 0。 实现与门和或门&#xff0c;权重为 1&#xff0c;阈值分别为 1…