【题解】《算法零基础100讲》(第48讲) 位运算 (左移)

一. 左移运算符

  左移运算符是一个二元运算符,它的计算形式是:a << b。意思就是a的二进制位向左移动b位(a,b都为整数),并在尾部增添0。
例如:

3 << 2
(0011)₂
(1100)₂

1.1 左移的结果

a << b = a * 2 ^ b

代码举例:

#include <stdio.h>

int main() {
    
    
	int a = 3;
	int b = 2;
	printf("%d\n", a << b);
	return 0;
}

输出结果为:12
12 = 3 * 2 ^ 2

1.2 负数左移的结果

  这里我们需要补充一点原码,反码,补码的知识,由于计算机的底层是不存在减法的,计算机只有加法,但我们要计算减法其实就是+该值的负数。但我们如何表示负数呢?

我们都知道,一个整型占4个字节的空间,也就是32个比特位,而每个比特位可以存放一个0或1,根据排列组合我们可以计算出由2 ^ 32次方个组合方式,也就是说可以表示2 ^ 32次方个数,但实际上int类型所能表示的正整数范围是[0,2 ^ 31 - 1],这是因为为了表示负数,我们用最高位的数代表符号,0位正,1位负。例如1和-1:
1: 0000 0000 0000 0000 0000 0000 0000 0001
-1:1000 0000 0000 0000 0000 0000 0000 0001
而这样得话就少了一位数了组成数字的位置只有31个位置。

但到这里肯定还要为,上面两个二进制相加并不等于0啊,这是怎么肥事呢?这是就要引入上面提的原码反码和补码了,我们在计算复数的时候,是要将负数转化位补码来计算的,整数的原码=反码=补码,但负数则需进行转化,转化方式如下(-1):
原码:1000 0000 0000 0000 0000 0000 0000 0001
反码:1111 1111 1111 1111 1111 1111 1111 1110(将原码的0变为1,1变为0符号位除外)
补码:1111 1111 1111 1111 1111 1111 1111 1111 (在反码的基础上加1)

1的补码:0000 0000 0000 0000 0000 0000 0000 0001
-1的补码:1111 1111 1111 1111 1111 1111 1111 1111
二者相加:0000 0000 0000 0000 0000 0000 0000
转化为原码后还是为0.

倒回负数的左移,通过上式我们知道负数的二进制表达方式(但我突然觉得补码反码提不提似乎都不影响━━( ̄ー ̄*|||━━)

-1 << 3

(10000000...0001)₂=>(10000000...1000)
我们可以发现和正数是一样的

1.3 负数左移的结果

  负数左移和正数一样,往尾部添0,但负数的符号位不会移动,如果负数一直左移,最终的结果为0.

二. 左移运算的作用

2.1 取模运算

m % y = m & ((1 << y) - 1)

2.2 作为标记码

我们通常将其与& 和 | 联用,对第i为进行标记: 1 << y

三. 相关练习

3.1 颠倒二进制位

190. 颠倒二进制位

我们通过按位或1将其最低为的数取出, 并放在rev的最高位, 然后再将第二位取出, 放在第二高位, 以此类推

代码如下:

uint32_t reverseBits(uint32_t n) {
    
    
    uint32_t rev = 0;
    for(int i = 0; i < 32 && n > 0; i++){
    
    
        rev |= (n & 1) << (31 - i);//将n的末尾位的数赋给rev的(31-i)位
        n >>= 1;//n右移1位
    }
    return rev;
}

3.2 2的幂

231. 2 的幂

判断是不是2的幂我们只需对其进行按位与计算, n & (n - 1), 如果结果位0, 则为2的幂,否则不是

代码如下:

bool isPowerOfTwo(int n){
    
    
    if(n <=0 ) return false;
    return !(n &(n-1));
}

3.3 数字的补数

476. 数字的补数

我们将1转为0, 0转为1即可

int findComplement(int num){
    
    
    for(int i = 0; num >> i; i++){
    
    
        num ^= 1 << i;
    }
    return num;
}

3.4 比特位计数

338. 比特位计数

我们通过kk &= (kk - 1);来统计1的个数

代码如下:

int* countBits(int n, int* returnSize){
    
    
    int *ret = (int*) malloc(sizeof(int) * (n + 1));
    *returnSize = n + 1;
    ret[0] = 0;
    for(int i = 1; i <= n; i++){
    
    
        int kk = i, sum = 0;
        while(kk){
    
    
            kk &= (kk - 1);
            sum++;
        }
        ret[i] = sum;
    }
    return ret;
}

Guess you like

Origin blog.csdn.net/qq_53060585/article/details/121781202