前言
众所周知,位运算是我们学计算机必学的东西,前人用二进制、位运算给我们了一个操作简单的计算机,但我们却很少接触位运算了。今天介绍一些位运算在算法中的运用。
位运算基础
&
按位与
如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
|
按位或
两个相应的二进制位中只要有一个为1,该位的结果值为1
^
按位异或
若参加运算的两个二进制位值相同则为0,否则为1
~
取反
~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1
<<
左移
用来将一个数的各二进制位全部左移N位,右补0
>
>
>>
>>
右移
将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数, 高位补0
奇技淫巧
1.技巧一:用于消去x的最后一位的1
x & (x-1)
x = 1100
x-1 = 1011
x & (x-1) = 1000
1.1.应用一 用O(1)时间检测整数n是否是2的幂次.
思路解析:N如果是2的幂次,则N满足两个条件。
1.N>0
2.N的二进制表示中只有一个1
一位N的二进制表示中只有一个1,所以使用N&(N-1)将唯一的一个1消去。
如果N是2的幂次,那么N&(N-1)得到结果为0,即可判断。
1.2.应用二 计算在一个 32 位的整数的二进制表示中有多少个 1.
思路解析:
由 x & (x-1) 消去x最后一位知。循环使用x & (x-1)消去最后一位1,计算总共消去了多少次即可。
1.3.将整数A转换为B,需要改变多少个bit位
思路解析
这个应用是上面一个应用的拓展。
思考将整数A转换为B,如果A和B在第i(0<=i<32)个位上相等,则不需要改变这个BIT位,如果在第i位上不相等,则需要改变这个BIT位。所以问题转化为了A和B有多少个BIT位不相同。联想到位运算有一个异或操作,相同为0,相异为1,所以问题转变成了计算A异或B之后这个数中1的个数。
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 2^0 = 1
运用位运算就可以很快的解出来。
因为2的幂次都是1,10,100,1000,10000
所以如果是2的幂次,则n&(n-1)=0
代码如下:
class Solution {
public:
bool isPowerOfTwo(int n) {
if(n <= 0 ) return false;
return !(n&(n-1));
}
};
例二:
求 a 的 b 次方对 p 取模的值。
输入格式
三个整数 a,b,p ,在同一行用空格隔开。
输出格式
输出一个整数,表示a^b mod p的值。
数据范围
1≤a,b,p≤109
输入样例:
3 2 7
输出样例:
2
解题思路1:
(暴力枚举) O(m)O(m)
暴力枚举也就是循环一遍即可
#include <iostream>
using namespace std;
#define ll long long //自定义ll为long long类型
int main()
{
ll a,b,c,ans=1;
cin>>a>>b>>c;
for (ll i=1;i<=b;i++)
ans=ans*a%c;
cout<<ans;
}
解题思路2:
大致思路:我们知道,任何一个自然数都可以写成
n=2^pi1+2^pi2+2^pi3+……2^pim
其中所有pi为非负整数,所以可以利用二分,将这一题次数m,转化一下即可
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define ll long long
ll n,m,k;
ll power(ll n,ll m,ll k)
{
ll ans=1%k;
while(m)
{
if (m&1)//如果m为奇数
ans=ans*n%k;
n=n*n%k;//将上一次的n进行平方
m>>=1;
}
return (ans%k);
}
int main()
{
cin>>n>>m>>k;
n%=k;
cout<<power(n,m,k);
}
十进制数转换为2进制数
while(n > 0){
arr[j] = (n & 1);
n >>= 1;
j = j + 1;
}
for(int i = j - 1;i >= 0 ;i--){
cout << arr[i];
}