今天学习的内容是位运算,位运算就是使用位操作符号操作二进制位。
按位与&
任何二进制位&上1都是本身,&0都是0,所以可以用来消除一个数的后五位,或者保留后五位,&可以用来判断奇偶性,因为偶数的二进制位最低为是0,奇数的最低位是1
按位与还可以用来计算一个数的二进制有多少个1,n&=(n-1),每次都会把n最低位的1消除,消除了几次最后变为全零,这个数就有几个1.
按位或 |
按位比较同位0,就是0,有一个是1,按位或完就是1,任何数或上0,都是本身。
按位异或^
相同为0,不同为1.
两个相同的数,异或完之后是0,任何数异或上0,都是本身。
位移运算符>>(右移位数) and <<(左移位数)
右移相当于除2,左移相当于乘2
右移高位补上符号位,左移低位补零高位直接丢弃。
题解
868. 二进制间距
思路:用循环遍历出每一位二进制位,然后如果这一个数的某一个二进制位第一次为1,那就先把它赋值给right,所以right初始值为-1,就是为了只给right首次的第一次赋值执行一次并且不计算长度,因为如果right为0,数字二进制位中只有一个1的话,就会计算错误。然后第二次遇到1的时候就计算间距,并且更新right
int binaryGap(int n){
int ans = 0;
int right = -1;
for(int i=0;i<32;i++)
{
if((n>>i)&1)
{
if(right==-1)
right = i;
else
{
ans = ans>(i-right)?ans:(i-right);
right = i;
}
}
}
return ans;
}
1734. 解码异或后的排列
首先题目条件,perm是n个正整数的排列,也即是,perm里面的n个数字就是从1到n的乱序。
而观察en数组,可以发现en[1]en[3]…en[n-2],最后相当于perm数组从1异或到n-1,此时我们将1到n异或到一起,然后再异或上上面的perm从1 到n-1的异或就得到了perm[0],然后迭代求出来后面的perm数组元素即可。
int* decode(int* encoded, int encodedSize, int* returnSize){
int*ans = (int*)malloc(sizeof(int)*(encodedSize+1));
*returnSize = encodedSize+1;
int ret = 0;
int sum = 0;
for(int i =1;i<=encodedSize+1;i++)
{
ret^=i;
}
for(int i =1;i<encodedSize;i+=2)
{
sum^=encoded[i];
}
ans[0] = sum^ret;
for(int i =1;i<encodedSize+1;i++)
{
ans[i] = ans[i-1]^encoded[i-1];
}
return ans;
}
89. 格雷编码
格雷码,n位格雷码,就是n位二进制位,对应数字范围是0–(2^n)-1,所以只需要将这些数字转换成对应的格雷码即可,格雷码的第i位是二进制的第i位异或上第i+1位。迭代求出来就行了。
int* grayCode(int n, int* returnSize){
int size = 1<<n;
int* ans = (int*)malloc(sizeof(int)*size);
for(int i =0;i<size;i++)
{
ans[i] = (i>>1)^i;
}
*returnSize = size;
return ans;
}
1238. 循环码排列
先得到格雷码,因为格雷码得第一位肯定是0,所以异或上start就是start,原本start得位置异或上start就变成了0,格雷码是循环的所以就完成了循环码的构建。
int* circularPermutation(int n, int start, int* returnSize){
int size = 1<<n;
int*ans = (int*)malloc(sizeof(int)*size);
for(int i=0;i<size;i++)
{
ans[i] = (i>>1)^i^start;
}
*returnSize = size;
return ans;
}
补充(五月)
191. 位1的个数
直接按位与,每次可以消除一个二进制位中的1,所以计数器++。
int hammingWeight(uint32_t n) {
int ans = 0;
while(n)
{
n&=(n-1);
ans++;
}
return ans;
}
461. 汉明距离
汉明距离就是计算这两个数字二进制位对应不相同的个数,先将连个数字异或起来,然后计算这个异或后的数字有几个1,就可以了
int hammingDistance(int x, int y){
int ans = 0;
int c = x^y;
while(c)
{
c &= (c-1);
ans++;
}
return ans;
}
136. 只出现一次的数字
相同数字异或之后是0,只要将所有数字异或到一起,剩下的那个数字就是只出现过一次的数字。
int singleNumber(int* nums, int numsSize){
int ans = 0;
for(int i =0;i<numsSize;i++)
{
ans^=nums[i];
}
return ans;
}
137. 只出现一次的数字 II
思路:出现3次的数字,二进制位必当是3的倍数,只要将数组元素的每一位取出来,加到一起,如果是3的倍数则不管,不是3的倍数,说明那个出现了1次的数字的这个二进制位是1,将1左移i位加到ret上,要注意这里左移的1要是无符号数,不然有符号int左移31位会报错。
要注意,有符号1不能左移31位,因为符号位被修改也就是符号位原来那个0,被移出去了,符号位的移入和移出都是未定义的行为,所以会报错。这里如果想要得到1左移31位的那个值就需要换成更大的类型,或者用无符号去掉符号位,下面那个强制转换,两种写法都可以。
int singleNumber(int* nums, int numsSize){
int ans = 0;
int ret = 0;
for(int i =0;i<32;i++)
{
ans = 0;
for(int j = 0;j<numsSize;j++)
{
ans+= (nums[j]>>i)&1;
}
if(ans%3!=0)
{
//ret+=((long long)1<<i);
ret+=((unsigned int)1<<i);
}
}
return ret;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNlbvNwl-1655026325313)(int左移31位报错问题.png)]