优选算法:位运算
位运算的一些基础概念
& :有0则为0
| :有1则为1
^ : 相同为0,相异为1;也可以称为无进位相加
>> << : 右移,左移
~:按位取反
一个数异或自己等于0,异或上0等于自己。
一个数取负数:n变为-n,要经过按位取反在加1。
提取最右侧的一个1:n&-n ,因为-n的本质是将最右侧的1的左侧全部变为和n相反的。
去掉最右侧的一个1:n&n-1 , 因为n-1需要最右侧的1来进行相减。
位图:就是将一个int的32位比特位看为一个数组,里面存储的东西就表示信息,这样就可能用一个变量就能解决。
判断字符是否唯一
算法思路
我们可以使用位图的思想,将字母映射到比特位上,其实只需要将字符串中的字母减去a,这样字符所对应到的数字就是0~26,我们只需要一个int类型的数字就可以处理了。当遍历到一个字母,我们就可以将其加入到位图中,判断一下这个位置是不是为1,如果是1就表示这个位置代表的数已经出现过,此时我们就直接返回false,是0的话就将这个比特位置为1,并继续遍历。
算法实现
public boolean isUnique(String astr) {if(astr.length()>26){return false;}int n = 0;for(int l = 0;l < astr.length();l++){int j = astr.charAt(l)-'a';if(((n>>j)&1)==1){return false;}else{ n|= 1<<j;}}return true;}}
丢失的数字
算法思路
这道题有多种实现,我们知道了数组的范围,就可以通过等差数列的求和公式求出总和,再将目标数组遍历求出总和,最后相减,就可以得到最后的结果。
但是我们也可以使用位运算的思路,将[0~n]的的数字都进行异或在一起,再将目标数组的所有数都一起异或,最后将两个数异或在一起,就可以得到缺失的那个数。
算法实现
public int missingNumber(int[] nums) {int j = 0;for(int k = 0;k<nums.length+1;k++){j^=k;}int l = nums[0];for(int k = 1;k<nums.length;k++){l^=nums[k];}return j^l;}
两数之和
算法思路
前面说到^是无进位相加,那么我们只需要处理没有进位的情况就能实现相加的情况了。首先我们需要将两数进行^,此时得到了两数无进位的和,而进位的情况出现在两比特位都为1,此时使用&求出都为1的情况,然后将1右移一位,就可以得到进位的情况了,此时我们再将两个结果进行异或,但是可能出现进位一位之后又出现两个1相加的情况,此时就又需要重复上面的操作。直到两者&后的数为0。
算法实现
public int getSum(int a, int b) {while(b!=0){int c = a^b;int d = (a&b)<<1;a = c;b = d;}return a;}
出现一次的数字二
算法思想
可以看到这个数组中出现的元素第一个每一个比特位其实是3n*1+1,3n*0+1,3n*1+0,3n*0+0,这四种情况,那么此时我们将每一个数的每一个比特位单独拿出来进行统计,得到的总数%3,那么余数肯定是单独出现的那一个数的比特位的值,我们通过一个0来进行统计每一位的比特位,最后返回的肯定是单独出现一次的数字。
算法实现
public int singleNumber(int[] nums) {int ret = 0;for(int j = 0;j<32;j++){int sum = 0;for(int num:nums){sum+=((num>>j)&1);}if(sum%3==1){ret|=(1<<j);}}return ret;}
消失的两个数字
算法思路
其实我们可以看成是在一个数组中想寻找两个只出现一次的数,我们可以先将这个数组中的所有数^起来,就会得到这两个数异或的结果,然后根据异或的性质,找到第一个两个数不一样比特位,以此为分界将数组分为两个,这样最后两组中都会有一个单独出现一次的数,此时再将数组中的数根据这个不一样的比特位区分,异或,得到结果。
算法实现
public int[] missingTwo(int[] nums) {int n = nums.length + 2;int num = 0;//处理创建的数组for(int k = 0; k<=n;k++)num^=k;//处理原数组for(int k:nums) num^=k; //找到不同的一位int i = 0;while(true){if(((num>>i)&1)==1){break;}else i++;}int [] arr = new int [2]; for(int j = 0;j<nums.length;j++){if(((nums[j]>>i)&1)==1){arr[0]^=nums[j];}else{arr[1]^=nums[j];}}for(int j = 1;j<=n;j++){if(((j>>i)&1)==1){arr[0]^=j;}else{arr[1]^=j;}}