自看位运算

刚开始刷力扣,发现位运算不太熟悉,写一个自看的位运算教程
1.与运算(&)
两个都为1结果才为1,其他为0

	1001
&   1010
——————————
    1000

2.或运算(|)
两个只要有1,结果就为1,两个都为0,结果才为0

	1001
&   1010
——————————
    1011

3.异或运算(^)
两个相同为0,不同为1

	1001
^   1010
——————————
    0011

4.取反(~)
对应位取反

~	1001
——————————
    0110

5.左移(<<)
高位丢弃,低位补0,相当于乘2

x << 16
移位前:1100 0101 1111 0000 1010 0011 0110 1000
移位后:1010 0011 0110 1000 0000 0000 0000 0000

6.右移(>>)和无符号右移(>>>)
如果高位为1,则补1,高位为0,则补0,相当于除2

x >> 16
移位前1100 0101 1111 0000 1010 0011 0110 1000
移位后1111 1111 1111 1111 1100 0101 1111 0000

奇淫技巧
1.交换a和b的值

void swap(a,b)
{
	a = a ^ b;
	b = a ^ b;
	a = a ^ b; 
}

2.位操作(&)来判断最后一位是1还是0,可用于判断奇偶

n = 1101
(n & 1) = 1

3.位操作(&)来判断第一位是1还是0

(n >> 31) & 1

右移到只剩下高位,再 &1,即可判断正负

4.交换符号,正数变负数,负数变正数
取反加1

(~n) + 1

5.将数取绝对值,整数不用变,负数需要取反加1,用上面的知识,先判断正负

int a = n >> 31;
if (0 == a)
	return n;
else
	return (~n)+1;

6.将高低位进行交换,这是在力扣习题上用的手法(无符号数)

n = 1100 0101 1111 0000 1010 0011 0110 1000
change = (n >>> 16) | (n << 16)
//这里很好理解,将高位无符号右移到低位,低位左移到高位,然后进行或运算

7.统计二进制中1的个数
如果用不断的右移来统计1的个数效率有点低,用一种高效的手法。
a&=(a-1)

a = 1110 0001 0001 0000
count = 0
进行a &= (a-1)
第一次:
	1110 0001 0001 0000
&	1110 0001 0000 1111
————————————————————————
	1110 0001 0000 0000
count = 1
第二次:
	1110 0001 0000 0000
&	1110 0000 1111 1111
————————————————————————
	1110 0000 0000 0000
count = 2
第三次:
	1110 0000 0000 0000
&	1101 1111 1111 1111
————————————————————————
	1100 0000 0000 0000
count = 3
第四次:
	1100 0000 0000 0000
&	1011 1111 1111 1111
————————————————————————
	1000 0000 0000 0000
count = 4
	1000 0000 0000 0000
&	0111 1111 1111 1111
————————————————————————
	0000 0000 0000 0000
count = 5

所以可以看到每次减1会将最后一个位1消掉,再和原数据相与变成0,有多少次运算,就有多少个1.代码如下

int count = 0;
while(a)
{
	a &= (a-1);
	count++;
}

这里给出力扣的题以及题解
颠倒给定的 32 位无符号整数的二进制位。

示例 1:

输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
     因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
示例 2:

输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
     因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。

第一个方法就是遍历,每次将最右边的位移到左边,可以想到开始时是将最右边的位向左移31位,然后加上后面右边第二位向右移30位,依次类推。

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        int power = 31;
        int temp = 0;
        while(n)
        {
            temp += (n&1) << power;
            n = n>> 1;
            power--;
        }
        return temp;
    }
};

有一种记忆化技术细节暂时没弄懂,先掠过
第三种就是分治,确实巧妙

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        n = (n >> 16) | (n << 16);
        n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);
        n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);
        n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);
        n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);
        return n;
    }
};

先将左16位和右16位交换,然后用&运算每一步分治,这里的细节让人一惊,真的强。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值