之前做过好多遍只出现一次的数字,当时学到了通过位运算解决
今天又看到一个 求两个单独的数字,也是通过位运算做到的,太狠了,这些大佬们
思考:
1.比如一组数 只有一个单独存在的数,其他都是两个,那么通过异或运算,剩下的就是这个数
2.现在又来一组数,有两个单独的数字,那么通过异或运算之后的结果,就是这两个数的异或结果
举个例子 一组数 1,2,1,3,2,5 单独的数是3和5
二进制分别是 11,101 他们 异或之后是110 也就是6
这时候看异或后的结果110 1的位置便是他们不相同的位数。
那么 通过这个原则,即可把完整的数组 分为两种情况,分别在两种情况中异或,
剩下的两个数就是要求的结果
赶快去LeetCode找到这个题 260题
本来想用C提交,但是传递三个参数,不太理解第三个参数干嘛的,便用java交了
int* singleNumber(int* nums, int numsSize, int* returnSize){
}
class Solution {
public int[] singleNumber(int[] nums) {
int [] res =new int [2];
int sum=0;
int q=1;
for(int i=0;i<nums.length;i++)sum^=nums[i];
while((q&sum)==0)q<<=1;
for(int i=0;i<nums.length;i++)
if((nums[i]&q)!=0)res[0]^=nums[i];
else res[1]^=nums[i];
return res;
}
}
写完之后看了下题解,哇,还有更巧妙的办法。。这里贴上官网的题解
class Solution {
public int[] singleNumber(int[] nums) {
int bitmask = 0;
for (int num : nums) bitmask ^= num;
int diff = bitmask & (-bitmask);
int x = 0;
for (int num : nums) if ((num & diff) != 0) x ^= num;
return new int[]{x, bitmask^x};
}
}
-bitmask=(~-bitmask+1);
x&-x 低位往高位走的第一个1
然后继续看LeetCode的137题
这个题是其中一个数出现一次,剩下的出现三次。求出出现一次的数
首先
第一种思路:把所有出现过的数放到一个set里面(可以排除相同的数)然后加起来乘以三
然后减去每一个数,那剩下的那个数再除以一个2,
但是。。。。 所有的数加起来int肯定是放不下的。至于long。算了 我们看下一个算法
第二种思路:位操作
假如例子是
1 2 6 1 1 2 2 3 3 3
1 -> 0 0 1
2 -> 0 1 0
6 -> 1 1 0
1 -> 0 0 1
1 -> 0 0 1
2 -> 0 1 0
2 -> 0 1 0
3 -> 0 1 1
3 -> 0 1 1
3 -> 0 1 1
看最右边的一列 1001100111 有 6 个 1
再往前看一列 0110011111 有 7 个 1
再往前看一列 0010000 有 1 个 1
我们只需要把是 3 的倍数的对应列写 0,不是 3 的倍数的对应列写 1
也就是 1 1 0,也就是 6。
这是因为 如果每一列出现三的倍数,那么,单独的那个数字转换二进制之后在这一列为0,也就是不存在,所以把这一列全部置0,同样 ,如果在之前不为0,就代表存在,置为1,然后输出即为最后正确答案
第三种思路
这种操作遇到原题还行,如果遇不到,我还是用上一种思路吧
class Solution {
public int singleNumber(int[] nums) {
int ones = 0, twos = 0;
for(int num : nums){
ones = ones ^ num & ~twos;
twos = twos ^ num & ~ones;
}
return ones;
}
}
这种思路有点不太理解,大体就是
比如状态转移 00》01》11》00
在第三次转移时候变回00
所以可以定义one two,three 三个变量通过位操作模拟,
但是three最后只保存一个状态,完全可以用前两个代替。