【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. }
相关文章
|
14天前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
7月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
191 2
|
7月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
362 73
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
246 10
|
8月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
7月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
296 3
|
8月前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
422 1
|
9月前
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
198 21
|
8月前
|
存储 算法 C++
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
|
10月前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
209 1