【C++】-- STL之位图(二)

简介: 【C++】-- STL之位图

三、位图实现

1.位图定义

位图只需要定义一个成员_bits即可,表明传入的数据个数:

1. #pragma once
2. #include<assert.h>
3. namespace delia
4. {
5.  template<size_t N>//数据总数
6.  class BitSet
7.  {
8.  private:
9.    vector<int> _bits;
10.   };
11. }

2.构造

1个字节可以表示32个数在不在,因此只需要申请N/32+1个空间,向上取整

1.  public:
2.    BitSet()
3.    {
4.      _bits.resize(N/32+1,0)//向上取整,否则不够存,默认每一位标记为0
5.    }

3.标记

只把该位置1,其他位都不变:

①计算x在位图中的第几个整数的第几个位置

②要把该位置1,就必须让该位和1按位或,且其他位都不能被改变

1.    //标记
2.    void Set(size_t x)
3.    {
4.      assert(x < N);
5. 
6.      //计算x映射的位在第i个整数
7.      //计算x映射的位在这个整数的第j位
8. 
9.      size_t i = x / 32;
10.       size_t j = x % 32;
11. 
12.       //_bits[i]的第j位标记为1,并且不影响其他位
13.       _bits[i] |= (1 << j);
14.     }

4.删除

只把该位置0,其他位都不变:

①计算x在位图中的第几个整数的第几个位置

②要把该位置0,就必须让该位和0按位与,且其他位都不能被改变

③把1左移j位再按位取反,就可以将该位置为0。

1.    //删除
2.    void Reset(size_t x)
3.    {
4.      assert(x < N);
5. 
6.      //计算x映射的位在第i个整数
7.      //计算x映射的位在这个整数的第j位
8. 
9.      size_t i = x / 32;
10.       size_t j = x % 32;
11. 
12.       //_bits[i]的第j位标记为0,并且不影响其他位,
13.       _bits[i] &= (~(1 << j));      
14.     }

5.检验在不在

①计算x在位图中的第几个整数的第几个位置

②返回该位和1左移j位置后相与的结果

1.    //检验在不在
2.    void Test(size_t x)
3.    {
4.      assert(x < N);
5. 
6.      //计算x映射的位在第i个整数
7.      //计算x映射的位在这个整数的第j位
8. 
9.      size_t i = x / 32;
10.       size_t j = x % 32;
11. 
12.       //如果第j位为1,结果是非0,非0为真
13.       //如果第j位为0,结果是0,0为假
14. 
15.       return _bits[i] & (1 << j);
16.     }

6.完整代码段

1. #pragma once
2. #include<assert.h>
3. #include<vector>
4. using namespace std;
5. 
6. namespace delia
7. {
8.  template<size_t N>
9.  class BitSet
10.   {
11.   public:
12.     BitSet()
13.     {
14.       _bits.resize(N/32+1,0)//向上取整,否则不够存,默认每一位标记为0
15.     }
16. 
17.     //标记
18.     void Set(size_t x)
19.     {
20.       assert(x < N);
21. 
22.       //计算x映射的位在第i个整数
23.       //计算x映射的位在这个整数的第j位
24. 
25.       size_t i = x / 32;
26.       size_t j = x % 32;
27. 
28.       //_bits[i]的第j位标记为1,并且不影响其他位
29.       _bits[i] |= (1 << j);
30.     }
31. 
32.     //删除
33.     void Reset(size_t x)
34.     {
35.       assert(x < N);
36. 
37.       //计算x映射的位在第i个整数
38.       //计算x映射的位在这个整数的第j位
39. 
40.       size_t i = x / 32;
41.       size_t j = x % 32;
42. 
43.       //_bits[i]的第j位标记为0,并且不影响其他位,
44.       _bits[i] &= (~(1 << j));      
45.     }
46. 
47.     //检验在不在
48.     void Test(size_t x)
49.     {
50.       assert(x < N);
51. 
52.       //计算x映射的位在第i个整数
53.       //计算x映射的位在这个整数的第j位
54. 
55.       size_t i = x / 32;
56.       size_t j = x % 32;
57. 
58.       //如果第j位为1,结果是非0,非0为真
59.       //如果第j位为0,结果是0,0为假
60. 
61.       return _bits[i] & (1 << j);
62.     }
63.   private:
64.     vector<int> _bits;
65.   };
66. 
67.   void test_BitSet()
68.   {
69.     BitSet<4294967295u>* bs=new BitSet<4294967295u>;//40亿个数据向上取整,即开辟2^32个bit位
70.   }
71. }

四、位图应用

1.给定两个文件,这两个文件分别都有100亿个整数,现在只有1G内存,如何找两个文件交集?

方案(1):依次读取第一个文件中的所有整数并标记映射到一个位图,再读取第二个文件中的所有整数,判断在不在位图,在就是交集中的书,不在就不是

方案(2):依次读取第一个文件的所有证书标记映射为位图1,一次读取第二个文件的所有证书标记映射为位图2,再对两个位图进行相与,结果为1的位就是交集。

无论方案(1)还是方案(2),由于整数最多只有2^32=4294967295个,42亿多个,文件有100亿个数据,肯定有重复的,因此只需要开辟2^32个bit位就能覆盖到这100亿个数据。

2.给定100亿个整数,设计算法找到只出现一次的整数

方案:位图是一个bit位标记一个值,现在用两个bit位标记一个值,位图能标记32个值在不在,那么现在只能标记16个值在不在:

00    出现0次

01    出现1次

10    出现2次及以上

11    不考虑

最后找出01标记的整数

1. #define  _CRT_SECURE_NO_WARNINGS  1
2. #pragma once
3. #include<assert.h>
4. #include<iostream>
5. #include<vector>
6. #include<bitset>
7. using namespace std;
8. 
9. int main()
10. {
11.   vector<int> v = { 3,67,259,1,7890,4,36,23 };
12. 
13.   bitset<4294967295u>* bs1 = new bitset<4294967295u>;
14.   bitset<4294967295u>* bs2 = new bitset<4294967295u>;
15. 
16.   for (auto e : v)
17.   {
18.     if (!bs1->test(e) && !bs2->test(e))//00->01,第一次出现
19.     {
20.       bs2->set(e);
21.     }
22.     else if (!bs1->test(e) && bs2->test(e))//01->10,第二次出现
23.     {
24.       bs1->set(e);
25.       bs2->reset(e);
26.     }
27.     else if (bs1->test(e) && !bs2->test(e))//10->11,第三次出现
28.     {
29.       //不用处理
30.     }
31.     else//第四次出现
32.     {
33.       //不用处理
34.       assert(false);
35.     }
36.   }
37. 
38.   for (size_t i = 0; i < 4294967295; i++)
39.   {
40.     if (!bs1->test(i) && bs2->test(i))//找01
41.     {
42.       cout << i << endl;
43.     }
44.   }
45.   return 0;
46. }

3.  1个文件有100亿个整数,1G内存,设计算法找到出现次数不超过2次的所有整数

现在用两个bit位标记一个值,

00    出现0次

01    出现1次

10    出现2次

11    出现3次

最后找出01和10标记的,即出现1次和出现2次的整数

1. #define  _CRT_SECURE_NO_WARNINGS  1
2. #pragma once
3. #include<assert.h>
4. #include<iostream>
5. #include<vector>
6. #include<bitset>
7. using namespace std;
8. 
9. int main()
10. {
11.   vector<int> v = { 3,67,259,1,7890,4,36,23 };
12. 
13.   bitset<4294967295u>* bs1 = new bitset<4294967295u>;
14.   bitset<4294967295u>* bs2 = new bitset<4294967295u>;
15. 
16.   for (auto e : v)
17.   {
18.     if (!bs1->test(e) && !bs2->test(e))//00->01,第一次出现
19.     {
20.       bs2->set(e);
21.     }
22.     else if (!bs1->test(e) && bs2->test(e))//01->10,第二次出现
23.     {
24.       bs1->set(e);
25.       bs2->reset(e);
26.     }
27.     else if (bs1->test(e) && !bs2->test(e))//10->11,第三次出现
28.     {
29.       bs1->set(e);
30.     }
31.     else//第四次出现
32.     {
33.       //不用处理
34.       assert(false);
35.     }
36.   }
37. 
38.   //找01或10
39.   for (size_t i = 0; i < 4294967295; i++)
40.   {
41.     if ((!bs1->test(i) && bs2->test(i))
42.       || (bs1->test(i) && !bs2->test(i))
43.     {
44.       cout << i << endl;
45.     }
46.   }
47. 
48.   return 0;
49. }
相关文章
|
3月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
126 10
|
7天前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
15 1
|
20天前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
36 7
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
66 4
|
2月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
81 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
63 2
|
2月前
|
存储 算法 Linux
【c++】STL简介
本文介绍了C++标准模板库(STL)的基本概念、组成部分及学习方法,强调了STL在提高编程效率和代码复用性方面的重要性。文章详细解析了STL的六大组件:容器、算法、迭代器、仿函数、配接器和空间配置器,并提出了学习STL的三个层次,旨在帮助读者深入理解和掌握STL。
62 0
|
23天前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
31 0
|
3月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
69 2
|
3月前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
90 5