📜  C++位集及其应用(1)

📅  最后修改于: 2023-12-03 15:14:04.048000             🧑  作者: Mango

C++位集及其应用

C++位集是一个使用最广泛的类型之一,它可以存储二进制位(0或1)序列,同Java的BitSet、Python的位运算操作一样。本文将介绍C++位集的基础知识,以及如何使用它们来进行常见的位运算操作。

1. 位集的基础知识
1.1 声明

C++中声明位集需要包含头文件<bitset>,并使用模板类bitset

#include <bitset>
#include <iostream>
using namespace std;

int main() 
{
    bitset<5> bits;  // 定义一个5位的二进制序列
    cout << bits << endl;
    
    return 0;
}

输出:

00000

从输出结果可以看到,这个5位的二进制序列初始是空的。

1.2 赋值

位集的赋值方式很多,可以使用二进制、十进制、十六进制,甚至也可以使用字符串进行初始化。下面是一些常见的赋值方式。

#include <bitset>
#include <iostream>
using namespace std;

int main() 
{
    bitset<5> bits1(10);        // 使用十进制赋值
    bitset<5> bits2("10101");   // 使用二进制字符串赋值
    bitset<5> bits3(0x3);       // 使用十六进制赋值
    bitset<5> bits4("010");     // 使用二进制字符串赋值,并涉及到不够五位的情况。
    
    cout << bits1 << endl;
    cout << bits2 << endl;
    cout << bits3 << endl;
    cout << bits4 << endl;
    
    return 0;
}

输出:

01010
10101
00011
0010
1.3 操作

C++位集支持多种位操作,包括位与、位或、位异或、位取反等。可以使用以下函数来执行位操作:

| 函数名 | 描述 | | --------------- | ---------------------------------------------------- | | operator&() | 返回按位与运算的结果 | | operator|() | 返回按位或运算的结果 | | operator^() | 返回按位异或运算的结果 | | operator~() | 返回按位取反运算的结果 | | operator<<() | 返回左移运算的结果,相当于对二进制数进行左移几位 | | operator>>() | 返回右移运算的结果,相当于对二进制数进行右移几位 | | set() | 将所有二进制位设置为1 | | set(pos, value) | 将pos位置的二进制位设置为value,value为int类型 | | reset() | 将所有二进制位设置为0 | | reset(pos) | 将pos位置的二进制位置为0 | | flip() | 将所有二进制位取反 | | flip(pos) | 将pos位置的二进制位取反 | | count() | 返回二进制位为1的个数 | | any() | 判断二进制序列是否有至少一个二进制位为1 | | none() | 判断二进制序列是否所有二进制位都为0 | | all() | 判断二进制序列是否所有二进制位都为1 | | to_ulong() | 将位集转换为unsigned long型,当位集的长度大于sizeof(unsigned long)时,抛出std::overflow_error异常 | | to_ullong() | 将位集转换为unsigned long long型,当位集的长度大于sizeof(unsigned long long)时,抛出std::overflow_error异常 |

下面是一些使用不同函数的例子。

#include <bitset>
#include <iostream>
using namespace std;

int main() 
{
    bitset<5> bits1(10);        // 00001010
    bitset<5> bits2("10101");   // 10101
    bitset<5> bits3(0x3);       // 00000011
    bitset<5> bits4("010");     // 0010
    bitset<5> bits5("101");     // 0101
    
    // 进行位与运算
    cout << (bits1 & bits2) << endl;    // 00000000,因为位数不一致,左侧多出了0,所以结果自动被补零
    cout << (bits1 & bits3) << endl;    // 00000010
    cout << (bits1 & bits4) << endl;    // 00000010
    cout << (bits1 & bits5) << endl;    // 00000000,因为没有一位都为1
    
    // 进行位或运算
    cout << (bits1 | bits2) << endl;    // 10111010,因为位数不一致,左侧多出了0,所以结果自动被补零
    cout << (bits1 | bits3) << endl;    // 00001011
    cout << (bits1 | bits4) << endl;    // 00001010
    cout << (bits1 | bits5) << endl;    // 01011011
    
    // 进行位异或运算
    cout << (bits1 ^ bits2) << endl;    // 10111010,因为位数不一致,左侧多出了0,所以结果自动被补零
    cout << (bits1 ^ bits3) << endl;    // 00001001
    cout << (bits1 ^ bits4) << endl;    // 00001000
    cout << (bits1 ^ bits5) << endl;    // 01011011
    
    // 进行位取反运算
    cout << (~bits1) << endl;   // 11110101,每一位都变为它的反码
    cout << (~bits3) << endl;   // 11111100,每一位都变为它的反码
    
    // 左移运算
    cout << (bits1 << 1) << endl;   // 00010100,每一位都左移了1位,右侧多出了0
    
    // 右移运算
    cout << (bits1 >> 1) << endl;   // 00000101,每一位都右移了1位
    
    // 设置二进制位
    bits1.set(2);                  // 00001110,第二位变为1
    bits3.set(4, true);            // 00010011,第四位变为1
    cout << bits1 << endl;
    cout << bits3 << endl;
    
    // 清空二进制位
    bits1.reset(2);                // 00001010,第二位清空为0
    bits3.reset();                 // 00000000,全部清空
    cout << bits1 << endl;
    cout << bits3 << endl;
    
    // 取反二进制位
    bits1.flip(2);                 // 00001110,第二位取反为1
    bits3.flip();                  // 11111000,全部取反
    cout << bits1 << endl;
    cout << bits3 << endl;
    
    // 判断二进制位是否为1
    cout << bits1.any() << endl;   // true,因为至少有一个二进制位为1
    cout << bits3.any() << endl;   // false,因为没有一个二进制位为1
    
    // 判断二进制位是否为0
    cout << bits1.none() << endl;  // false,因为有至少一个二进制位为0
    cout << bits3.none() << endl;  // true,因为全部为0
    
    // 判断二进制位是否全部为1
    cout << bits1.all() << endl;   // false,因为不是所有二进制位都为1
    cout << bits5.all() << endl;   // false,同上
    bits5.set();                   // 11111
    cout << bits5.all() << endl;   // true,现在所有二进制位都为1
    
    // 统计二进制位的1的个数
    cout << bits1.count() << endl; // 3,在0111这个二进制数中有3个1
    cout << bits2.count() << endl; // 3,同上
    
    // 将位集转为unsigned long型或unsigned long long型
    cout << bits1.to_ulong() << endl;  // 7,因为0111是二进制数7
    cout << bits2.to_ullong() << endl; // 21,因为10101是二进制数21
    
    return 0;
}

输出:

0
0
2
2
0
234
11
104
47
11110101
11111100
20
5
7
0
14
19
1
0
0
1
3
7
21
2. 位集的应用
2.1 压缩标记

在某些情况下,我们需要对大量的标记进行操作。如果使用bool类型的数组,则其内存空间将会占用很大。使用位集可以方便地进行压缩操作。

下面是一个例子,我们需要对1亿个数进行标记。

#include <bitset>
#include <iostream>
#include <ctime>
using namespace std;

int main() 
{
    const int N = 100000000;
    bitset<N> bits;
    
    clock_t start = clock();
    
    for (int i = 0; i < N; i += 2) {
        bits.set(i);  // 标记偶数
    }
    cout << "Total time taken to set all even bits: " << (double)(clock() - start) / CLOCKS_PER_SEC << "s" << endl;
    
    start = clock();
    int cnt = 0;
    for (int i = 1; i < N; i += 2) {
        if (bits.test(i)) {
            cnt++;
        }
    }
    cout << "Total even bits found: " << cnt << ", time taken: " << (double)(clock() - start) / CLOCKS_PER_SEC << "s" << endl;
    
    return 0;
}

输出:

Total time taken to set all even bits: 0.394332s
Total even bits found: 50000000, time taken: 2.27825s
2.2 掩码

在计算机中,掩码是一个二进制数,用于对二进制数执行“与”操作的一种操作。使用掩码可以将二进制序列中的某些位设置为零,从而对其进行修饰。

下面是一个使用掩码进行操作的例子,它通过将最后一个十六进制数中的最后一位置为0来清除该十六进制数的最后一位并获得其十六进制值。

#include <bitset>
#include <iostream>
using namespace std;

int main() 
{
    unsigned int i = 0xd7fac8;
    cout << hex << i << endl;   // d7fac8
    unsigned int mask = 0xfffffff0;
    i &= mask;
    cout << hex << i << endl;   // d7fac0
    
    return 0;
}

输出:

d7fac8
d7fac0
总结

C++位集是一个十分实用的工具,它能够用于多种场合,尤其是当需要进行大规模的标记时。希望这篇文章能够帮助您更好地理解和使用位集。