找出数组中只出现一次的数(位运算的使用)

位运算技巧解LeetCode
本文探讨了位运算在解决LeetCode题目中的应用,包括只出现一次的数字II和只出现一次的数字III。通过巧妙地利用位运算,尤其是异或操作,解决了数组中特定数字的查找问题,提供了一种高效且新颖的解决方案。

位运算的使用

题一:leetcode137 只出现一次的数字 II

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

采用统计出现次数的思想,重点是:当其出现三次应当当做其没有出现过。

两比特位即可标识三种状态,即两比特位即可标识数组中某元素出现0、1、2次三种状态。我们可以采用两个变量代表这两个比特位。这样做有一个好处:可以存储那个只出现一次的那个数值。

现在我们考虑数组中某元素的每个比特位的可能情况,即:

变量a变量b某元素的某个比特更新后的变量a更新后的变量b
00000
01001
10010
00101
01110
10200

上面考虑的是数组中某元素的某一个比特位可能的情况,数组中每个元素的每个比特位都进行上面情况的考虑,最终的变量b就是我们所求的那个只出现一次的数。

如果那个只出现一次的数出现了两次,那么最终的变量a就是那个只出现两次的数。

下面的代码我们考虑数组中所有元素出现5次,但只有一个数字出现1次。

nums = [1, 2, 4, 5] * 5 + [3] * 1

a, b, c = 0, 0, 0
for d in nums:
    a_ = (~a & b & c & d) + (a & ~b & ~c & ~d)
    b_ = (~a & ~b & c & d) + (~a & b & ~c & d) + (~a & b & ~c & ~d) + (~a & b & c & ~d)
    c_ = (~a & ~b & ~c & d) + (~a & b & ~c & d) + (~a & ~b & c & ~d) + (~a & b & c & ~d)
    a, b, c = a_, b_, c_
# 其中c就是只出现一次的数字3
print(a, b, c)
# 如果数字3出现2次,那么变量b就是这个只出现2次的数字
# 如果数字3出现3次,那么变量b和变量c就是这个只出现3次的数字
# 如果数字3出现4次,那么变量a就是这个只出现4次的数字

当然,我们可以在这道题的基础上继续延伸,比如数组中所有元素出现k次,只有一个元素出现n(0<n<k)次数,我们都可以采用上面的思路进行求解。当然我们需要的变量数量就需要m(2**m>=k)个。

题二:leetcode260 只出现一次的数字 III

给定一个非空整数数组,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

这题我们采用题一的思想是不可行的,因为我们所求的是两个数,两个数最终每个比特位上统计之后无法还原成两个数。

因为除了那个只出现一次的两个数,其余的数都出现了偶数次,所以我们可以采用两两抵消的思想进行,将出现偶数次的数字都抵消掉。如何才能让出现偶数次的数抵消掉呢?我们可以采用位的异或运算抵消掉出现偶数次的数。

最终,我们得到两个只出现一次的数的异或值,如何利用这个异或值得到最终的两个只出现一次的数呢?可以找到这个异或值为1的最位,然后通过这个位来区分两个数,通过遍历数组中所有的数,即可得到最终的只出现一次的两个数。

nums = [1, 2, 4, 5] * 2 + [3, 7]

tmp = 1
for num in nums:
    tmp ^= num

# 得到为1的最低位
# mask = 1
# while mask & tmp == 0:
#     mask <<= 1
mask = tmp & -tmp

x, y = 0, 0
for num in nums:
    if num & mask:
        x ^= num
    else:
        y ^= num
print(x, y)
# 如果上面出现奇数次的数只有一个,那么得到的x、y其中有一个为0

如上,我们通过这个方式即可求解类似的问题,只要出现偶数次数的数都可以采用异或抵消掉,然后即可通过两个或者一个出现奇数次的数的异或值,最终求解得到出现奇数次的两个数或者一个数。

<think>好的,我需要帮助用户解决如何在数组中查找重复出现三的元素的问题。用户之前的问题是关于找出数组中的最大值,现在转向查找重复三的元素,可能是在处理据去重或统计问题。 首先,回顾用户提供的引用。引用[1]和[4]讨论了在三重复数组中找只出现一次的元素,使用的是位运算方法。而用户现在的问题相反,是要找重复三。这可能意味着数组中的其他元素出现不同,比如一次或两,但用户并没有明确说明。需要先确认问题细节,但用户可能希望假设其他元素只出现一次,或者可能有其他情况。 引用[2]中的Java代码通过双重循环统计每个元素的出现,但时间复杂度是O(),对于大数组不适用。引用[3]是关于检测是否有重复元素,返回布尔值,但用户需要的是具体找出重复三的元素。 用户的需求可能是在一个数组中,大部分元素出现一次,而有一个或多个元素出现三,需要找出这些元素。或者,可能所有元素都出现三,除了一个出现一次,但用户的问题相反。需要明确,但根据用户提问,他明确说“重复出现3”,所以目标是找出所有出现三的元素。 接下来,考虑不同的解决方案: 1. **哈希表统计法**:遍历数组,用哈希表记录每个元素的出现,最后筛选出为三的元素。这种方法时间复杂度O(n),空间复杂度O(n),但用户可能希望更优的空间复杂度,比如O(1),但用户并没有明确要求空间限制,所以需要提供这个方法作为基础解法。 2. **排序遍历法**:先排序数组,然后遍历检查相邻元素是否相同,统计。时间复杂度O(n log n),空间复杂度O(1)(如果原地排序)。虽然时间不如哈希表,但空间更优。 3. **位运算优化法**:参考引用[4]中的位运算方法,原本用于找出一次的元素,但需要调整来找出出现三的元素。可能需要统计每个位上1的出现,如果能被3整除则不属于目标元素。不过这种方法通常适用于找出一次的元素,而其他出现三的情况,需要重新考虑如何适配。 用户可能希望高效的方法,尤其是处理大据时。需要比较各种方法的优缺点。 另外,用户之前的回答中提到了时间复杂度O(n)的方法,比如查找最大值,所以可能倾向于线性时间的解法。 现在需要组织回答,先介绍哈希表法,再讨论排序法,然后探索位运算的可能性。不过位运算在这里可能不太直接适用,因为目标元素是出现三,而其他元素可能不同。需要进一步分析。 可能位运算的方法需要重新设计,比如每个位统计总出现,如果某位总模3不等于0,则说明目标元素在该位上有1。但这种方法适用于其他元素出现三,而一个元素出现一次的情况。如果反过来,其他元素出现一次,目标出现三,那么总位模3余3的话,可能无法区分。所以可能需要另一种方法。 或者,如果数组中有多个元素出现三,而其他出现一次,那么位运算可能需要统计每个位的总,然后模3等于0的位属于出现三的元素的位。但需要多个元素的情况,可能需要更复杂的处理。 可能这里位运算并不直接适用,所以可能需要先推荐哈希表和排序法,再探讨可能的位运算优化,但需要说明其限制。 另外,需要考虑代码示例,比如Python、Java等,用户之前的示例中有Python和C,所以可能需要提供Python的实现。 最后,生成相关问题,引导用户进一步思考,比如如何处理空间限制,或更高效的算法。</think>### 查找数组中重复出现三元素的方法 #### 一、问题分析 假设数组中有若干元素重复三,其余元素出现非三(如出现一次或两)。需要**在时间复杂度 $O(n)$、空间复杂度尽量低**的条件下找到所有重复三的元素。 --- #### 二、解决方案对比 ##### 1. 哈希表统计法(通用解法) **核心思路**: 利用哈希表记录每个元素的出现,最后筛选出计为3的元素。 **时间复杂度**:$O(n)$ **空间复杂度**:$O(n)$ **Python示例**: ```python def find_three_times_elements(arr): counter = {} for num in arr: counter[num] = counter.get(num, 0) + 1 return [k for k, v in counter.items() if v == 3] # 示例 arr = [2, 3, 2, 5, 2, 3, 3, 5, 5] print(find_three_times_elements(arr)) # 输出 [2, 3, 5] ``` ##### 2. 排序遍历法(空间优化) **核心思路**: 先排序数组,再遍历统计连续相同元素的量。 **时间复杂度**:$O(n \log n)$(主要来自排序) **空间复杂度**:$O(1)$(若原地排序) **Python示例**: ```python def find_three_times_elements_sorted(arr): arr.sort() result = [] i = 0 while i < len(arr): count = 1 while i + 1 < len(arr) and arr[i] == arr[i + 1]: count += 1 i += 1 if count == 3: result.append(arr[i]) i += 1 return result ``` ##### 3. 位运算优化法(特定场景) **适用条件**: 若数组中**其他元素均出现一次**,仅有一个元素重复三,可基于引用[4]的思路改进。 **核心逻辑**: 统计每个二进制位上1的总出现,若某位的总模3余0,则该位在目标元素中为0,否则为1。 **学表达**: 对每个比特位,设总出现为 $S$,则: $$ \text{目标元素在该位的值} = \begin{cases} 1 & \text{if } S \bmod 3 \neq 0 \\ 0 & \text{otherwise} \end{cases} $$ **Python示例**: ```python def find_single_three_element(arr): result = 0 for bit in range(32): # 假设为32位整 count = 0 for num in arr: if (num >> bit) & 1: count += 1 if count % 3 != 0: result |= (1 << bit) return result # 示例:仅一个元素重复三,其余出现一次 arr = [5, 3, 5, 1, 5, 3] print(find_single_three_element(arr)) # 输出 1 ``` --- #### 三、方案选择建议 | 方法 | 优势 | 局限性 | |--------------|--------------------------|---------------------------| | 哈希表统计法 | 实现简单,支持多目标元素 | 空间复杂度 $O(n)$ | | 排序遍历法 | 空间复杂度低 | 时间复杂度高(依赖排序) | | 位运算优化法 | 时间和空间均为 $O(n)$ | 仅适用于单一目标元素的场景 | --- #### 四、扩展思考 1. **如何处理多个重复三的元素?** 位运算方法需扩展为记录每个比特位的计模3后的余,但需额外处理多个目标元素的组合情况[^4]。 2. **如何优化哈希表的空间?** 使用布隆过滤器(概率型据结构)压缩存储,但可能产生误判[^1]。 3. **是否支持流式据?** 位运算方法天然支持流式处理,只需逐元素更新比特位计器。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值