位运算专题
1、_15:二进制中1的个数
题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
/**
* 1、对于整数n:
* n&1=1:说明n的二进制数最后一位为1
* n&1=0:说明n的二进制数最后一位为0
*
* 、算法:
* 让n&1,根据结果计数
* n>>>1,让n无符号右移1位
*/
public class Solution {
public int NumberOf1(int n) {
int res = 0;
while (n!=0){
res += n&1;
n>>>=1;
}
return res;
}
}
2、_50:第一个只出现一次的字符位置
题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str.length()==0 || str==null){
return -1;
}
HashMap<Character,Integer> map = new HashMap<>();
for(int i=0;i<str.length();i++){
if(map.keySet().contains(str.charAt(i))){
map.put(str.charAt(i),map.get(str.charAt(i))+1);
}else{
map.put(str.charAt(i),1);
}
}
for(int i=0;i<str.length();i++){
if(map.get(str.charAt(i))==1){
return i;
}
}
return -1;
}
}
方法2:使用数组统计字符出现的次数:
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str.length()==0 || str==null){
return -1;
}
int[] arr = new int[256];
for(char c:str.toCharArray()){
arr[c]++;
}
for(int i=0;i<arr.length;i++){
if(arr[str.charAt(i)]==1){
return i;
}
}
return -1;
}
}
3、_56:数组中只出现一次的数字
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路1:可以通过Hash表来写,但是时间和空间复杂度太高了,因为先要通过for循环判断字符出线的次数,还要for循环判断只出现一次的字符。
思路2:由于数组中存在着两个数字不重复的情况,我们将所有的数字异或操作起来,最终得到的结果是这两个数字的异或结果:(相同的两个数字相互异或,值为0)) 最后结果一定不为0,因为有两个数字不重复。
4 ^ 1 ^ 4 ^ 6 => 1 ^ 6
6 对应的二进制: 110
1 对应的二进制: 001
1 ^ 6 二进制: 111
但是无法通过异或结果111来得出最终的两个不同的数组,因此需要分组:
通过 & 运算来判断一位数字不同即可分为两组,那么我们随便两个不同的数字至少也有一位不同吧!
我们只需要找出任意一位不同的数字,即可完成分组操作。
由于两个数异或的结果就是两个数数位不同结果的直观表现,所以我们可以通过异或后的结果去找一位不同的数字mask,所有的可行 mask 个数,都与异或后1的位数有关。只要异或后的位数为1,就可作为分组条件,就说明两个数可以分到两个不同的组。
num1: 101110 110 1111
num2: 111110 001 1001
num1^num2: 010000 111 0110
可行的mask: 010000 001 0010
010 0100
100
为了操作方便,我们只去找最低位的mask:
num1^num2: 010000
mask: 000001
000000
mask: 000010
000000
mask: 000100
000000
mask: 001000
000000
mask: 010000
000000
mask: 010000 :根据mask的1对应的位将数组中的数分成两组,在这位上为0的分到一组,为1的分到1组
num1: 101110 :mask为1的位上为0
num2: 111110 :mask为1的位上为1
代码:
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
// 0与任何数异或都是他本身
int k=0;
//将所有的数异或,得到异或的结果
for(int num:array){
k ^= num;
}
int mask = 1;
//寻找mask,即异或后的结果的最后一位为1的mask
while ((k & mask)==0){
mask <<= 1;
}
// 0与任何数异或都等于这个数本身
num1[0]=0;
num2[0]=0;
for(int num:array){
if((num&mask)==0){
num1[0]^=num;
}else {
num2[0]^=num;
}
}
}
}
4、_65:不用加减乘除做加法
题目:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
public class Solution {
public int Add(int num1,int num2) {
if (num2 == 0) {
return num1;
}
// 转换成非进位和 + 进位
return Add(num1 ^ num2, (num1 & num2) << 1);
}
}