位运算的那些奇技淫巧 | 掌(装)握(逼)必备,妙解两道算法题

看完本文,可以顺便解决leetcode以下两个题目:
136.只出现一次的数字(简单)
191.位1的个数(简单)

一、常(装)见(逼)的位操作
先看几个有意思的位操作:

或操作 | 和空格将英文字符转换为小写,小写本身还是小写

(‘a’ | ’ ') = ‘a’
(‘A’ | ’ ') = ‘a’

与操作 & 和下划线将英文字符转换为大写,大写本身还是大写

(‘b’ & ‘’) = ‘B’
(‘B’ & '
’) = ‘B’

n&(n-1) 消除数字 n 的二进制表示中的最后一个 1

异或^和空格进行英文字符大小写互换;判断异号

(‘d’ ^ ’ ') = ‘D’
(‘D’ ^ ’ ') = ‘d’
x^y < 0;异号,否则同号

下面我们从简单开始,一步一步的实现复杂的位运算~

1、判断奇数偶数

看到这个,你肯定觉得很简单,然后随手写下:

if (n % 2 == 0)
{	// 偶数
} else {	// 奇数
}

其实使用位运算,可以这样写

if ( n & 1 == 1)
{
	// 奇数
} else {
	// 偶数
}

因为如果一个二进制表示偶数的话

偶数的最后一位肯定是0;

奇数的最后一位肯定是1;

2、交换两个数字

这个也是很常见的操作,很简单的就可以写下来:

temp = x;
x = y;
y = temp;

但是如果在实际工作中,遇到不借助辅助空间,依旧让你去交换两个数字呢?
这个时候,位运算的优势就体现出来了
位运算实现如下:

x = x^y
y = x^y
x = x^y

看的第一眼,肯定很懵~ 啥玩意儿这是

首先,我们需要知道两个个基础的公式:

n^n =0; 即相同的两个数异或结果是0
n^0 = n;一个数和0异或的结果是本身

所以,上面展开一下,就是

x = x^y
y = x^y^y = x^0 = x
x = x^y = x^y^x = x^x^y = 0^y = y
3、找出没有重复的数字

我们以上面的为基础,即相同的两个数异或结果是0;一个数和0异或的结果是本身
那我们就把这一些数字,遍历一遍,全部异或一下,不就OK了?

4、m的n次方

要求一个数m的n次方,但是不能使用POW函数,如何做?
不要使用 m x m x m x m x m~ 一直乘以n次,太low了;
时间复杂度就是 O(n)

使用阶乘,复杂度可以降低到O(log N)


int pow(int n){
    int sum = 1;
    int tmp = m;
    
    while(n != 0){
    
        if(n & 1 == 1){
            sum *= tmp;
        }
        
        tmp *= tmp;
        n = n >> 1;
    }
    return sum;
}

上述代码要是不理解二进制可能你就看不懂,这里我来举例解释一下

比如,求m的 5次方
我们把5用二进制表示,就是 0101,
从右往左看,第一个1 代表一个m
第二个1代表一个m x m x m x m ;
当二进制的这一位有1的时候,才把该位置代表的累乘的数算上,否则不算,继续看下一位的情况;

5、判断一个数是不是二的指数

如果一个数,是二的指数,那么它的二进制表示,肯定只有一个1
结合上面说到的:n&(n-1) 消除数字 n 的二进制表示中的最后一个 1

问题就好解决了

return (n > 0 ) && (n & (n - 1) == 0) ) ;
6、找出不大于N的最大2的幂指数

假设这个数是 19;
19用二进制表示就是 00010011
我们需要的结果就是 16 ;00010000
也就是 把 0010011,只保留最左边的一个1;
这个思路很简单,就是把最左边的1后面位,全部变成1;然后整体加1;由于加1之后会进位,所以在整体右移一位

在这里插入图片描述
应该怎么最左边的1后面位,全部变成1呢?

n |= n >> 1;
n |= n >> 2;
n |= n >> 4;

n |= n >> 1;
结果是最左边的1和其右边,也会有一个1;11

n |= n >> 2;
结果是最左边的11和其右边,也会有一个11;1111

n |= n >> 4;
结果是最左边的1111和其右边,也会有一个11;11111111

如此这样,后面就都变成1了,然后+1,右移一位就行

二、leetcode解题
136.只出现一次的数字(简单)

这个题刚刚上面说过了,全部都异或
直接上答案;

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        //异或
        int n=nums.size();
        int res=0;
        for(int i=0;i<n;i++)
        {
            res^=nums[i];
        }
        return res;
    }
};

191.位1的个数(简单)

在这里插入图片描述
思路:
只需要记得:
n&(n-1) 消除数字 n 的二进制表示中的最后一个 1


class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res = 0;
        while (n != 0) {
            n = n & (n - 1);
            res++;
        } 
        return res;
    }
};

在这里插入图片描述

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值