leetcode 位运算
遇见题目是能够想到位运算这件事本身有一定的难度,但是一旦想到这个的方法,题目解答会变得比较简单。
首先复习一下在java中的位运算符号。
java中的位运算一般有:
^ 异或
& 与
|或
~ 非
右移>> 常用(右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1)
无符号右移>>(无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0)
<<左移(没有无符号左移)
本文中涉及到的题目大都与异或有关,简单说下异或的用法。给一堆数,例如这些数中只有一个数一次出现,或有两个数一次出现。异或运算可以在一堆偶数次出现的数里面筛选出一个单数次出现的数。(进行全员异或操作即可)
一个数出现一次,比较简单,直接全员异或题136
class Solution {
public int singleNumber(int[] nums) {
int k=0;
for(int num:nums){
k ^= num;
}
return k;
}
}
两个数只出现一次题260
package singlenum;
public class Solution {
public static void main(String[] args){
int[] nums = new int[]{1,1,2,3,5,3};
int[] answers = new Solution().singleNums(nums);
System.out.print(answers[0]+" "+answers[1]);
}
public int[] singleNums(int[] nums){
int mask=1;
int k=0;
for(int num:nums){
k ^= num;
}
while(( k & mask ) == 0){
mask <<= 1;
}
//用于分组,mask确定了两个数不一样的地方
int a=0,b=0;
for(int num:nums){
if((num & mask)==0){
a ^= num;
}else{
b ^= num;
}
}
return new int[]{a,b};
}
}
有一个数出现三次题137 如果采用位运算,比较难想到但十分简单,另给出一种常规解法。
package singlenum;
import org.omg.PortableInterceptor.IORInterceptor;
public class Solution137 {
public static void main(String[] args){
int[] nums = new int[] {1,1,1,3};
int answer = new Solution137().singleNumbers(nums);
System.out.print(answer);
}
public int singleNumbers(int[] nums){
// int ones = 0;
// int twos = 0;
// for(int i=0;i<nums.length;i++){
// ones = ones^nums[i]&~twos;
// twos = ones^nums[i]&~ones;
// }
// return ones;
if(nums.length == 0) {
throw new IllegalArgumentException();
}else if(nums.length == 1) {
return nums[0];
}else {
//一个int型的数32位,array数组存储的是一比特
//根据题意,array数组的元素是要么是3的倍数要么不是3的倍数
int[] array = new int[32];
int answer = 0;
//
for(int i = 0;i < nums.length;i ++) {
//采用无符号右移,无符号右移高位补0,有符号右移负数高位补1
for(int j = 0;j < 32;j ++) {
//array[j] = (nums[i] >>> j) & 1;
array[j] += (nums[i] >>> j) & 1;
}
}
/*
for(int number : array) {
System.out.println(number);
}
*/
//因为原数组的数是有符号的,因此要考虑array[31] % 3是1还是0
//下面遍历array的[0,30]不是[0,31]
for(int i = 0;i < 31;i ++) {
if(array[i] % 3 != 0) {
//answer += (2 << i);
answer += (1 << i);
}
}
if(array[array.length - 1] % 3 == 1) {
return -1 * ((int)Math.pow(2,31) + 1 - answer);
}else {
return answer;
}
}
}
}
题645
这个同样可以采取位运算,就转化为了有一个数出现一次,一个数出现三次(合并下标),然后查找来分辨这两个数。
class Solution {
public int[] findErrorNums(int[] nums) {
int num_change[] = new int[20005];
for(int i = 0;i<nums.length*2;i++){
if(i < nums.length){
num_change[i]=nums[i];
}else{
num_change[i] = i - nums.length + 1;
}
// System.out.print(num_change[i]);
}
int k = 0;
for(int numc:num_change){
k ^= numc;
}
int mask = 1;
while((k&mask)==0){
mask <<= 1;
}
int a = 0,b = 0;
for(int numc:num_change){
if((numc & mask)==0){
a ^= numc;
}else {
b ^= numc;
}
}
for(int num:nums){
if(num == a){
return new int[]{a,b};
}
if(num == b){
return new int []{b,a};
}
}
throw new IllegalArgumentException("NO ANSWER");
}
}
参考链接