描述
给你一个 非空 整数数组 nums
,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1] 输出:1
示例 2 :
输入:nums = [4,1,2,1,2] 输出:4
示例 3 :
输入:nums = [1] 输出:1
提示:
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
- 除了某个元素只出现一次以外,其余每个元素均出现两次。
碎碎念:你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。意指时间复杂度只能是 O(n) 或更小;且不开辟其他可迭代空间,仅能用常量,即空间复杂度为O(1)
低效代码
class Solution:
def singleNumber(self, nums: List[int]) -> int:
for item in set(nums):
nums.remove(item)
if item not in nums:
return item
利用count函数同样低效
转自csdn用户Python热爱者 转自csdn用户
一、使用字典 dict 统计
循环遍历出一个可迭代对象的元素,如果字典中没有该元素,那么就让该元素作为字典的键,并将该键赋值为1,如果存在则将该元素对应的值加1。
二、使用 collections.defaultdict 统计
defaultdict(parameter) 接受一个类型参数,例如:int、float、str 等。
传递进来的类型参数,不是用来约束值的类型,更不是约束键的类型,而是当键不存在时,实现一种值的初始化。
defaultdict(int) – 初始化为0
defaultdict(float) – 初始化为0.0
defaultdict(str) – 初始化为’’
三、List count方法
count() 方法用于统计某个元素在列表中出现的次数。
四、使用集合(set)和列表(list)统计
先用 set 去重,然后循环把每一个元素和对应的次数 list.count(item) 组成元组。
五、collections.Counter方法
Counter 是一个容器对象,使用 collections 模块中的 Counter 类可以实现 hash 对象的统计。
Counter 是一个无序的容器类型,以字典的键值对形式存储,其中元素作为 key,其计数作为 value。
计数值可以是任意的 Interger(包括0和负数)。
Counter() 对象还有几个可调用的方法:
most_common(n) – TOP n 个出现频率最高的元素
elements – 获取所有的键 通过list转化
update – 增加对象
subtrct – 删除对象
下标访问 a[‘xx’] --不存在时返回0
妙解
解题思路:
题目要求时间复杂度 O(N) ,空间复杂度 O(1) ,因此首先排除 暴力法 和 辅助哈希表法 。
设整型数组 nums 中出现一次的数字为 x ,出现两次的数字为 a,a,b,b,... ,即:
nums=[a,a,b,b,...,x]
异或运算有个重要的性质,两个相同数字异或为 0 ,即对于任意整数 a 有 a⊕a=0 。因此,若将 nums 中所有数字执行异或运算,留下的结果则为 出现一次的数字 x ,即:
a⊕a⊕b⊕b⊕...⊕x
= 0⊕0⊕...⊕x
= x
代码:
异或运算满足交换律 a⊕b=b⊕a ,即以上运算结果与 nums 的元素顺序无关。代码如下:
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
x = 0
for i in range(len(nums)): # 1. 遍历 nums 索引,使用引用类型
x ^= nums[i] # 2. 对当前索引的值执行异或运算
return x # 3. 返回出现一次的数字 x
7种位运算的详细特性和示例:
- 与运算(AND):
- 任何数和0做与运算,结果是0,即 x & 0 = 0。例如,5(101) & 0 = 0。
- 任何数和其自身做与运算,结果是自身,即 x & x = x。例如,5(101) & 5(101) = 5(101)。
- 或运算(OR):
- 任何数和0做或运算,结果是自身,即 x | 0 = x。例如,5(101) | 0 = 5(101)。
- 任何数和其自身做或运算,结果是自身,即 x | x = x。例如,5(101) | 5(101) = 5(101)。
- 异或运算(XOR):
- 任何数和0做异或运算,结果是自身,即 x ^ 0 = x。例如,5(101) ^ 0 = 5(101)。
- 任何数和其自身做异或运算,结果是0,即 x ^ x = 0。例如,5(101) ^ 5(101) = 0。
- 异或运算满足交换律和结合律,即 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c。例如,5(101) ^ 3(011) ^ 4(100) = 5 ^ (3 ^ 4) = (5 ^ 3) ^ 4。
- 非运算(NOT):
- 非运算会反转操作数的所有位。例如,~5(101) = 2(010)。
- 左移运算(SHL):
- 左移n位等于乘以2的n次方,即 x << n = x * 2^n。例如,5(101) << 2 = 20(10100)。
- 左移运算不改变操作数的符号位。
- 逻辑右移运算(SHR):
- 右移n位等于除以2的n次方,即 x >> n = x / 2^n。例如,20(10100) >> 2 = 5(101)。
- 逻辑右移运算会用0填充移位后产生的空位。右边(低位)舍去,左边(高位)补0
- 算术右移运算(SAR):
- 算术右移运算会用符号位填充移位后产生的空位,因此它可以保持负数的符号。例如,对于负数-5(1011) >>> 2 = -2(1110)。右边(低位)舍去,左边(高位)补原符号位,原符号位为0就补0,为1就补1(负数);
这些特性使得位运算在计算机科学和编程中有很多有趣和有用的应用。例如,我们可以用位运算来进行快速的乘法和除法运算,或者用异或运算来交换两个变量的值,等等。