机试复习笔记--位运算

一、位运算作用

在程序中适当使用位运算可以提高程序运行效率。

例如,判断程序是否为奇数

if(i % 2 == 1)    // 判断i是否为奇数

可以用位运算语句进行代替

if(i & 1 == 1)    // 判断数是否为奇数

 还有乘2和除2的运算操作

i *= 2
i /= 2

可以由位移操作代替

i <<= 1;    // 左移运算,相当于乘2
i >>= 1;    // 右移运算,相当于除2

用位运算代替原始的运算操作,在操作数量级很大的情况下,可以节省相当的时间。

二、用位运算解题

对于部分算法题目,我们可以从位运算的角度求解

leetcode 136 只出现一次的数字

在一个数组中找出只出现一次的一个数字,其余数字均会出现两次。

这道题目可以用位运算中的异或(^)操作求解,异或运算的原则为位相同为0,相异为1,所以两个相同的数异或为0(n^n=0),一个数和0异或操作数值不变(n^0=0^n=0),所以只要将数组中的数全部进行异或操作即可得到解。

int singleNumber(vector& nums) {
    int n = 0;
    for(int i=0; i

 leetcode 231 2的幂

判断一个数是否为2的幂

2的幂的数字的二进制形式为1000...(n个0),该数字减1就变成了0111111(n个1),我们发现减去1后每一位都相反,所以判断他们相与(&)为0即可。

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

三、位运算题目

部分试题直接考察位运算的操作

leetcode 190 颠倒二进制位

uint32_t reverseBits(uint32_t n) {
    uint32_t ans=0;
    
    int i=32;
    while(i--)
    {
        ans <<= 1;
        ans += n&1;    // 复制当前n中的最后一位
        n >>= 1;
    }
    return ans;
}

相似题目 leetcode 476 数字的补数 

四、使用库简化位操作(C++)

在部分情况下,可以使用C++的bitset库来处理位操作

4.1 引入头文件

#include

4.2 bitset初始化

构建bitset时需要传入一个正整数N代表比特的位数

template  class bitset;

构建bitset

bitset<8> b1;   // 比特位全0 00000000
bitset<8> b2(3);    // 十进制数字初始化,代表该数字的二进制 00000011
bitset<8> b3("01111001");  // 字符串初始化 01111001
bitset<8> b4("0101111001");  // 字符串初始化,如果长度超出则截取前n位 01011110
bitset<8> b5(0xa);  // 十六进制数字初始化 00001010

4.3 bitset位访问

bitset<8> b;
bitset<8> b2;
cout << b[1] << endl;   // 访问第n个比特位 0
b[1] = 1;
b[2] = b[1];
cout << "修改后的b为:" <<  b << endl;    // 00000110

// count操作
cout << "b中1的个数为:" << b.count() << endl;    // 2
// size
cout << "b的长度为:" << b.size() << endl;   // 8
// 求0
cout << "b中0的个数为:" << (b.size() - b.count()) << endl;   // 6

// test返回bitset第n位的是否为1
cout << b.test(0) << ' ' << b.test(1) << endl;  // 0 1
// any bitset中是否有任意一位为1
cout << b.any() << endl;    // 1
cout << b2.any() << endl;   // 0
// none bitset是否全为0
cout << b.none() << endl;   // 0
cout << b2.none() << endl;  // 1

4.4 bitset位操作

bitset<4> b; // 0000
// set
cout << b.set() << endl;       // 全部设置为1 1111
cout << b.set(2,0) << endl;    // 将第2位设置为0 1011
cout << b.set(2) << endl;      // 设置第2位为1 1111
// reset
cout << b.reset(1) << endl;    // 将第1位设置为0 1001
cout << b.reset() << endl;     // 全部设置为 0000
// flip
cout << b.flip(2) << endl;      // 第2位取反 0100
cout << b.flip() << endl;       // 全部位取反 1011

4.5 运用

bitset可以相当简化的bool数组,但是占用的内存更加少,因为8个比特位才占一个字节

bitset<100000> b;
bool b2[100000];
printf("%d\n", sizeof(b));
printf("%d\n", sizeof(b2));

输出结果

12500
100000

不过bitset的缺点也是很明显的,在定义时必须确定变量的代销,不能采用动态的值,长度也不能像vector那样动态变化,不过需要大长度布尔型数组时,可以在算法题中采用bitset节约内存,例如筛法求素数中的数组,第n位上的值代表第n个数是否为素数

bitset<100001> b;

b.set();    // 全部设置为1
b.reset(0); // 第0位设置为0
b.reset(1); // 第1位设置为0 代表1不是素数
for (int i = 2; i

输出n位二进制字符串

bitset可以用来偷懒地输出二进制字符串

bitset<8> b(12);
string s = b.to_string();   // 转换为字符串 00001100
cout << s << endl;
cout << bitset<8>(011) << endl; // 八进制初始化 00001001
cout << bitset<8>(0xa) << endl; // 十六进制初始化 00001010

利用bitset解题

leetcode 762 二进制表示中质数个计算置位

(这里仅仅为了演示bitset用法)

int countPrimeSetBits(int L, int R) {
    bitset<32> b;
    b.set(2); 
    b.set(3);         
    b.set(5);
    b.set(7);
    b.set(11);
    b.set(13);
    b.set(17);
    b.set(19);
    b.set(23);
    b.set(29);
    b.set(31);
        
    int c=0;
    for(int i=L; i<=R;i++)
    {
        bitset<32> bits(i);
        if(b[ bits.count() ]) {
            c++;
        }
    }
    return c;
}

参考资料

http://www.cplusplus.com/reference/bitset/bitset/

你可能感兴趣的:(算法,机试,数据结构与算法分析)