目录
位运算符的介绍
位运算妙解
位运算高效解N皇后问题
实际项目中的位运算应用
位运算符的介绍
- 左移操作<<
将a的二进制表示的每一位向左移动b位,左边超出的位截掉,右边不足的补0.
A=5。A的二进制表示为110.
A<<2= 11000.
- 右移操作>>和>>>
>>将二进制数按指定右移几位,移掉的省略,左边遗失的位,(该数是正数)用0补齐,(该数是负数)用1补齐。
>>>将二进制按指定右移几位,移掉的省略,左边缺失的位,用0补齐。
- 按位与操作 A&B
按位与操作 A&B 将A和B的二进制表示的每一位进行与操作,只有两个对应的二进制位都为12时,结果为才返回1,否则返回0.
A=10。二进制:001010 B=44二进制:101100。
A = 001010
B = 101100
A&B =001000
- 按位或操作 A|B
将A和B的二进制每一位进行或操作,只要两个对应的二进制位有一个为1,结果就为1,否则为0。
A = 001010
B = 101100
A|B=101110
- 按位异或操作A^B
A和B 的二进制表示的每一位进行异或操作,如果对应的二进制位不同,则结果为1,否则为0.
A = 001010
B = 101100
A^B=100110
- 按位非操作 ~ A
将A的二进制表示每一位进行取反操作,如果对应的二进制位为0,结果位为1,否则为0.
A=00001011
~A = 11110100
位运算妙解
- 数组查非重
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你的算法应该具有线性复杂度,你可以不使用额外的空间来实现吗?
实例一:
输入[2,2,1] 输出:1
实例二:
输入[4,1,2,1,2] 输出:4
题目解析:
根据题目描述,由于加上了时间复杂度必须是O(n),并且空间复杂度为O(1)的条件,因此不能用排序方法,也不能使用map数据结构。
//两个相同的数异或等于0
public static int funtion(int[] a) {
for (int i = 1; i < a.length; i++) {
a[0]=a[0]^a[i]; //返回数组中全部元素异或
}
return a[0];
}
- 数组查非重进阶版
进阶版 有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两 次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关) 即O(1)。 示例 :输入: [1,2,2,1,3,4] 输出: [3,4]
解题思路:数组中全部数字异或得到的应该是两个不同的数直接异或结果。该结果二进制中1的位置的为两不同数不相同的位置。再将该位置以1,0讲数组分为两组,分别求每组全体异或结果,两个结果即为两个不同的数。
public static int[] funtion(int[] a) {
int[] result= new int[2];
int res=a[0];
for (int i = 1; i < a.length; i++) {
res^=a[i];
} //全部整数异或结果
int bitIntex=0;
for (int i = 0; i < 32; i++) {
if((res>>i & 1)== 1) {
bitIntex=i;
break;
}
} //获取第一个异或结果不为0的位
for (int i = 0; i < a.length; i++) {
if ((a[i]>>bitIntex & 1)==1)
result[0]^=a[i];
else
result[1]^=a[i];
} //根据位分别位1还是0将数组分为两组,分别异或
return result;
}
- 数组查非重再进阶
题目描述:数组中,只有一个数出现一次,剩下都出现三次,找出出现一次的数
因为数是出现三次的,也就是说,对于每一个二进制位,如果只出现一次的数在该二进制位为1,那么这个二进制位在全部数字中出现次数无法被3整除。
5 0101 从右边第三位数值(1)加起来
4 0100 后不能被三整除,则单独数在此二
5 0101 进制为上为1,,加起来能被三整除
5 0101 的,单独数在该二进制位为0
public static int findNum(int[] num) {
int result=0;
for (int i =0 ; i < 32; i++) { //二进制最多32位
int bit=0; //记录每个数在 二进制第i位 加起来的数值 初始位0
for (int j = 0; j < num.length; j++) {
bit+=((num[j]>>i)&1); //bit 每个数在 i位置的(1)和
}
result |=((bit%3)<<i); //result 为每个位置 bit%3 后相按位或
}
return result;
}
- 2的幂次方
题目来源于 LeetCode 上第 231 号问题:2 的幂。题目难度为 Easy,目前通过率为 45.6% 。
题目描述:给定一个整数,编写一个函数来判断它是否为2的幂次方。
首先,分析一下2的幂次方数的二进制写法1 2 4 8 16 …… 1 10 100 1000 10000 ……
仔细观察,可以看出 2 的次方数都只有一个 1 ,剩下的都是 0 。根据这个特点,只需要每次判断最低位是否为 1 ,然后向右移位,最后统计 1 的个数即可判断是否是 2 的次方数。
public static boolean isPowerTwo(int n) {
int cnt=0;
while(n>0) {
cnt+= n&1;
n>>=1;
}
return cnt==1? true:false;
}
该题还有一种巧妙的解法。再观察上面的表格,如果一个数是 2 的次方数的话,那么它的二进数必然是最高位为1,其它都为 0 ,那么如果此时我们减 1 的话,则最高位会降一位,其余为 0 的位现在都为变为 1,那么我们把两数相与,就会得到 0。
1000
& 0111
------
0000
public static boolean isPowerOfTwo(int n) {
return (n>0)&&(n&(n-1))==0;
}
- 4的幂次方
给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方。(不使用循环或者递归来完成本题)
十进制 | 二进制 |
---|---|
2 | 10 |
4 | 100(1在第三位) |
8 | 1000 |
16 | 10000(1在第五位) |
32 | 100000 |
64 | 1000000(1在第七位) |
128 | 10000000 |
256 | 100000000(1在第九位) |
规律:4的幂次方的数的二进制1的位置都是在奇数位,解题思路:** 先判断该书是否为2的幂,如果是,再在2的幂基础上判断是否为4的幂。判断方法:令该数与特殊数**“1010101010101010101010101010101”** 异或,如果结果是它本身,则该数为4第幂次数。特殊数的16进制写法为0x55555555
public static boolean isPowerOfFour(int num) {
if(num <=0)
return false;
//先判断是不是2的幂
if((num & num-1)!= 0)
return false;
if((num & 0x55555555)==num) {
return true;
}
return false;
}
- 交换数值大小
交换两个数相信很多人天天写过,我也相信你每次都会使用一个额外来变量来辅助交换,例如,我们要交换 x 与 y 值,传统代码如下:
int tem=x;
x=y;
y=tem;
这样写有问题吗?没问题,通俗易懂,万一哪天有人要为难你,不允许你使用额外的辅助变量来完成交换呢?你还别说,有人面试确实被问过,这个时候,位运算就来了。代码如下:
a=a^b;
b=a^b;
a=a^b;
位运算支持交换律,即a^b^a=a^a^b=b.
位运算高效解N皇后
问题描述: 八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。
static int upperlimit;
static int count;
public static void main(String[] args) {
int n=5;
upperlimit=(1<<n)-1;
fun(0, 0, 0);
System.out.println(count);
}
static void fun(int row,int ld,int rd) {
int pos,p;
if(row!=upperlimit) {
pos=upperlimit&(~(row|ld|rd));
while(pos!=0){
p= (~pos+1) & pos;
pos-=p;
fun((row|p), (ld|p)<<1, (rd|p)>>1);
}
}else {
count++;
}
}
代码先呈上,若本帖阅读超一千,我会补齐这一块的解释 嘻嘻嘻