位运算的应用。
一、数组中只有一个数出现1次,其他数字均出现两次。找出只出现1次的那一个数字。
任何数字和本身异或的结果为0。0和某数字的异或结果为其本身。所以数组中所有数字异或结果就是所求结果。代码如下。
int singleNumber1(vector<int>& nums) {
int ans=nums[0];
for(int i=1;i<nums.size();i++){
ans^=nums[i];
}
return ans;
}
二、数组中有两个数字出现1次,其他数字均出现两次。找出只出现1次的那两个数字。
这里还是要借鉴第一节中的思路。如果把一个数组分成两个数组,每个数组中分别包含一个只出现1次的数字即可使用第一节中的思路。那么如何把数组分成两个子数组呢?如何两个只出现1次的数组为a和b,那么整个数组的异或结果为xor_res,那么xor_res=a^b。xor_res不等于0。取xor_res二进制表示中最后一位为1的位置(假设第k位,结果九为1<<k),说明a和b在第k位不相同。我们根据第k位是否为1将数组分成两个部分,对每部分分别求只出现1次的数字即可。
int lastBitOf1(int num){
return num&(~(num-1));
}
vector<int> singleNumber2(vector<int>& nums) {
int xor_res=0;
for(int i=0;i<nums.size();i++){
xor_res^=nums[i];
}
int pos=lastBitOf1(xor_res),one=0,two=0;
for(int i=0;i<nums.size();i++){
if(nums[i]&pos){
one^=nums[i];
}else{
two^=nums[i];
}
}
vector<int>result_vec;
result_vec.push_back(one);result_vec.push_back(two);
return result_vec;
}
三、数组中有三个数字出现1次,其他数字均出现两次。找出只出现1次的那三个数字。
这里的方法很难想出来。http://zhedahht.blog.163.com/blog/static/25411174201283084246412/里面讲的挺好。这里我的实现与他的实现有些不同。他本身的证明后面也有点麻烦。
大致思路为:假设要求数字为a、b和c,整个数组异或结果为xor_res,xor_res=a^b^c。可证xor_res和a、b、c均不相等。(反证法,如果xor_res=a,则a^a=a^b^c^a=0,则b^c=0,说明b和c相等,矛盾)。所以xor_res^a,xor_res^b,xor_res^c均不为0。
f(x)取x最后1位为1的位置。f(xor_res^a)^f(xor_res^b)^f(xor_res^c)不为0。(因为前两部分异或结果的二进制表示只可能有0个1或者2个1,而第三部分二进制表示只有1个1)
f(xor_res^a)^f(xor_res^b)^f(xor_res^c)既然不为0,假设最后第k位为1。f(xor_res^a)、f(xor_res^b)、f(xor_res^c)三部分中只能有一个第k位为1。(三部分也可以写成f(b^c)、f(a^c)和f(a^b),如果每部分第k位都为1,很容易推出矛盾)。
那么三部分中只能有一个第k位为1。铺垫了这么久,也就是为了根据这个先找出一个。然后使用第二节中的方法找出其他两个。
void singleNumber3(vector<int>&nums){
int xor_res=0;
for(int i=0;i<nums.size();i++){
xor_res^=nums[i];
}
int temp=0;
for(int i=0;i<nums.size();i++){
temp^=lastBitOf1(xor_res^nums[i]);
}
int pos=lastBitOf1(temp),first=0;
vector<int>temp_vec;
for(int i=0;i<nums.size();i++){
if(lastBitOf1(xor_res^nums[i])==pos){
first^=nums[i];
}else{
temp_vec.push_back(nums[i]);
}
}
vector<int>result_vec=singleNumber(temp_vec);
cout<<first<<endl;
cout<<result_vec[0]<<endl;
cout<<result_vec[1]<<endl;
}
四、数组中有一个数字出现1次,其他数字均出现三次。找出只出现1次的那一个数字。
这里就无法再使用前面三节中异或运行了。我们换种思路来想,把每个数字用做二进制表示。int的话只需要32位即可,所以我们新建大小为32的数组,把每个数字的对应位加到数组的相应位置上。如果我们把只出现1次的数字(假设为a)去除,那么最后数组里面的每一位都应该是3的倍数。所以最后数组里任一个位模3之后就是a二进制表示中相应位的值。
int singleNumber(vector<int>& nums) {
vector<int>vec(32,0);
for(int i=0;i<nums.size();i++){
for(int j=0;j<32;j++){
vec[j]+=(nums[i]>>j)&1;
}
}
int ans=0;
for(int i=0;i<32;i++){
ans+=((vec[i]%3)<<i);
}
return ans;
}