参考链接
- https://leetcode-cn.com/problems/single-number-ii/
- https://leetcode-cn.com/problems/single-number-ii/solution/zhi-chu-xian-yi-ci-de-shu-zi-ii-by-leetc-23t6/
题目描述
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
解题思路
排序+双指针
最简单的方法应该是用额外空间来记录每个数字出现的次数。但是尽量向进阶版靠拢,不用额外空间。那就是数组上的遍历问题,让数组有序就比较好解了。排序后,可以使用双指针,一前一后,比较两个指针的值,如果不相等且两指针距离等于1,那就是答案。但使用了排序,所以时间复杂度是不满足线性要求的。
进阶版
这种方法就比较清奇了,使用逻辑运算符。遍历数组,对每个数的二进制位相加,再对3取余,就得到了答案。因为答案以外的数都出现了3次,其二进制位和对3的余数肯定为0。
但如果依次处理每一位,时间复杂度达不到O(n)。为了将数作为一个整体同时处理,设计了两个数a、b来存储遍历数组时,依次相加的余数。即分别用00、01、10表示0、1、2。这样,如果一个数在某一位为1,出现3次后对应的ab会变成00,但是答案只出现一次,所以对应的ab会是01;如果一个数在某一位为0,那么无论出现多少次,对应的ab都是00。遍历完数组后,只有答案的1会被保留下来,同时a肯定都是0,只需要输出b就行。
由真值表写出逻辑表达式的方法:a)从真值表内找输出端为“1”的各行,把每行的输入变量写成乘积形式;遇到“0”的输入变量上加非号;2)把各乘积项相加,即得逻辑函数的表达式。
本题的逻辑表达式为
代码
排序+双指针
class Solution {
public:
int singleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());
int res = 0;
int i = 0;
for (int j = 1; j < nums.size(); j ++)
{
if (nums[j] != nums[i])
{
if (j - i == 1)
{
res = nums[i];
break;
}
else
{
i = j;
}
}
}
if (i == nums.size() - 1)
{
res = nums[i];
}
return res;
}
};
进阶版
class Solution {
public:
int singleNumber(vector<int>& nums) {
int a = 0, b = 0;
for (int num: nums) {
tie(a, b) = pair{(~a & b & num) | (a & ~b & ~num), ~a & (b ^ num)};
}
return b;
}
};