前言
掌握位运算,可以打开我们求解问题的思路,在某些时候,使用位运算可以方便我们解决算法问题,因此对于一些位运算的小妙招我们需要掌握。
注:本文所有例题都来源与leetcode
1、0/1==不选/选
- 说明:对于一个二进制数,它的每一位只可能是0或者1,也就是只有两种可能,我们可以把只有两种可能这一特点转换成选/不选这两种情况,这在子集问题上有着很好的应用。
- 题目:78.子集
文字说明:给你一个整数数组 nums,数组中的元素互不相同 。返回该数组所有可能的子集(幂集)。
解集不能包含重复的子集。你可以按任意顺序返回解集。 - 分析:我们都知道,一个有n个元素的集合,它的子集个数为2^n个。
为什么是2^n呢?我们可以用排列组合的思想来思考:对于集合里的每一个数,我们都有2种做法,将其加入子集/不将其加入子集,因此对于n个元素,我们就有了 2^n个存放方法。而这2种做法刚好可以抽象成0/1,这 2^n刚好对应 0~2^n-1这些不同的数,因此我们就可以想到二进制+位运算的思想来解题了。
比如:集合{1,3} 子集:{},{3},{1},{1,3},正好对应0(00),1(01),2(10),3(11) - 代码
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> subsets = new ArrayList<>();//结果集
int bmp = (int) Math.pow(2, nums.length);
// 从 nums.length 个 0 遍历到 nums.length 个 1
for (int i = 0; i < bmp; i++) {
List<Integer> subset = new ArrayList<>();
for (int j = 0; j < nums.length; j++)
// 将1依次左移j位,与i相与,判断该位是否为1,
//如果为1,则相与结果不为0,否则为0
if ((i&(1<<j)) !=0) subset.add(nums[j]);
subsets.add(subset);//将当前子集加入结果集中
}
return subsets;
}
}
2、同消异留
- 说明:异或:对于两个数a,b和ab异或的结果c;a和b异或时判断a和b每一位上的数字是否相同,若该位数字相同则c中该位为0,否则为1。因此我们可以很容易得到结论:如果a和b相等,则a和b异或的结果c=0。
- 题目:136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:你的算法应该具有线性时间复杂度并且不使用额外空间 - 分析:既然除了一个数字出现了1次,其余数字都出现了两次,那么干脆让这些数字依次异或下去,异或的结果就是答案(异或过程中出现两次的数字都抵消了)。
- 代码
class Solution {
public int singleNumber(int[] nums) {
int x=nums[0];
for (int i = 1; i <nums.length ; i++) {
x^=nums[i];//依次异或数组中每一个数字
}
return x;
}
}
3、“与”判公共
-
说明:与:对于两个数a和b和ab与运算的结果c,a和b与时判断a和b每一位上的数字是否均为1,若该位数字均为1则c该位为1,否则为0。因此我们可以很容易得到结论:如果a和b没有一位上都为1,则a和b与的结果c=0。
-
题目:318. 最大单词长度乘积
给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。 -
分析:本题的关键在于怎么判断两个单词不含有公共字母,结合上述说明,我们只需要将出现的字符用二进制数表示出来,并用与运算就能判断。那怎么用二进制数表示每个单词中出现的字符呢?注意到本题只出现小写字母,只有26位,刚好可以用int类型表示,如101表示出现字母a,c。
-
代码
class Solution {
public int maxProduct(String[] words) {
int []bitwise=new int[words.length];
for (int i = 0; i <words.length ; i++) {
//得到每个单词出现的字符,用cur表示
//cur的二进制数的某位为1,代表该字符在该单词中出现
//如cur第0位为1,表示a在单词中
int cur=0;
for (char c:words[i].toCharArray()){
cur|=(1<<(c-'a'));//或运算,统计字符是否出现
}
bitwise[i]=cur;
}
int ans=0;
for (int i = 0; i <words.length-1 ; i++) {
int len=words[i].length();
for (int j = i; j <words.length ; j++) {
if((bitwise[i]&bitwise[j])==0){//第i和第j个单词与操作
//如果为0,代表它们没有重复元素,计算最大值
ans=Math.max(ans,len*words[j].length());
}
}
}
return ans;
}
}