找出数组中只出现1次的数(各种变形)

来源:互联网 发布:手机淘宝查看交易快照 编辑:程序博客网 时间:2024/04/30 06:58

位运算的应用。

一、数组中只有一个数出现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;}

3 0
原创粉丝点击