136. 只出现一次的数字
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
解题
位运算(时间复杂度N,空间复杂度1)
异或:位相同则0,相异则1;
所以对一个数自身异或,返回0;
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res=0;
for(int num:nums)
res^=num;
return res;
}
};
从0开始对每个num逐个异或,若异或到相同元素,会抵消异或效果,若异或到单个元素,即变为单个元素;
137. 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解题
数字分为出现1次和三次的,有限状态自动机: 每一位分为三种状态——无数字(0),有一次数字(1),有两次数字(0),有三次数字(0);
注意观察 once 只有两种情况下转移后为 1 。一种是 once=0, twice=0, x=1 ,另一种是 once=1, twice=0, x=0 。其他所有情况下 once 都转移为 0 。这两种情况都满足 x^once=1 并且 twice=0 ,所以 once 的转移就是 once = (x^once) & (~twice) 。
同理,观察 twice 只有两种情况下转移后为 1 。一种是 once=1, twice=0, x=1 ,另一种是 once=0,
twice=1, x=0 。其他所有情况下 twice 都转移为 0 。这两种情况都满足 x^twice=1 并且 once^twice=1
,所以 twice 的转移就是 twice = (x^twice) & (once^twice) 。但是!!! once
已经抢先一步转移过了,所以值已经变掉了,一个解决方法就是用临时变量保存一下前一个状态的 once 值。另一个方法就是,这两种情况下,once
都会转移到 0 ,所以判断条件直接用转移后的 once=0 就行了,随后转移就是 twice = (x^twice) & (~once) 。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int once=0,twice=0;
for(auto x:nums)
{
once = (once^x)&(~twice);
twice = (twice^x)&(~once);
}
return once;
}
};
第一次出现:放入once,twice=非once&once=空;
第二次出现:once抵消为空,放入twice;
第三次出现:once=~twice&twice=空,twice抵消为空;
故为三次循环,保留第一次出现的到once,第二次第三次出现全部抵消,第四次又归为第一次出现;
解法2
逐位计算,计算int 32位每一位的1的个数,余3即为单个的数的那一位的位数;
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res=0;
int r;
for(int i=0;i<32;i++)
{
r=0;
for(auto num:nums)
{
r+=1&(num>>i);
}
res|=(r%3)<<i;
}
return res;
}
};
注意点
判断num第i位是否为1,需要1&(num>>i),若写(1<<i)&num),则会影响i后面的位数,因为1<<i相当于第i位为1,其他位为0!
面试题56 - I. 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
限制:
2 <= nums <= 10000
解题
一组数字中找到两个单个出现的数;
将数据分为两部分——一部分包含第一个单个数字:另一部分包含另一个单个数字,然后用位运算即可分别找到两个单个数字;
如何将两个单个数字分开?
找到两个数不同的一位;
res=0,与nums中每一位进行异或操作,得到任何一位为1,则说明两个单个数在该位不相同!
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int res=0;
for(int num:nums)
res ^=num;
int h=1;
while((h&res)==0) //找到两个数不等的那一位
h<<=1;
int a=0;
int b=0;
for(int num:nums)
{
if(num&h)
//大于0就行,说明该位置有数
a ^=num;
else b ^=num;
}
vector<int> result={a,b};
return result;
}
};
num&h为一类;!(num&h)为一类,则一类含一个数;
且相同数字都在同一组中;
注意点
位运算分优先级,记得加括号();