DataStruct-Bitset

75 阅读3分钟

Bitset


概述

位图主要适用于大量的数据处理,它强调的是利用比特位来标志对应数据的出现,是一种哈希形式。


类的实现

成员变量

由于数据需要映射到对应比特位,故仍需要开辟对应大小的空间,且必须支持随机访问,故采用 vector 实现。(deque 的随机访问比 vector 要慢,因为它实际上是通过链表 + 顺序表的方式实现的,故随机访问需要进行一系列计算和定位,故这里不采用 deque)而 vector 可以采用整型家族的任意类型实现,例如 char 是八个比特位,int 是 32 个比特位,只需要做好对应处理即可。在本文中将采用 char 进行叙述。

namespace Thepale
{
	template <size_t N = 32>
	class bitset
	{
	private:
		std::vector<char> _v;
        int _count;
    };
}

这里采用了非类型模板参数,具体是希望用户在使用时可以直接指明需要储存的数据个数,而采用什么数据类型实现是不必指明的(例如使用 char 或 int)。由于同时需要检索已经使用的比特位个数,故添加一个 _count 计数。

成员函数

构造函数

构造函数需要完成空间的开辟,通常这一过程只需要调用 vector 的 resize 即可完成,需要注意的是除以对应数据的比特位后还需要加一,这是因为这里的空间并不能采用去尾法,而需要采用进一法,因为任何一个比特位都是有效的,不能随意舍弃。

bitset()
{
    _v.resize(N / 8 + 1, 0);
}

由于本类中封装的是库中的 vector,不需要完成拷贝构造或析构等函数的实现。

test

此成员函数的功能是检查 pos 位置的比特位是 0 还是 1,这需要一定位运算技巧:

bool test(size_t pos)
{
    assert(pos < N);

    return _v[pos / 8] & (1 << (pos % 8));
}

首先找到该数据所在的 char,然后将 1 左移到对应位,可知这个左移的 1 除了一个 1 之外全部为 0,若 pos 所在的位为 0,则与后的结果为 0,若 pos 所在的位为 1,则与后结果不为 0,而逻辑值不为 0 即为 1。

set

set 需要将对应位设置为 1。故必然面临两种情况:1.当前位已经为 1;2.当前位为 0,需要设为 1。在已经为 1 的情况下,不需要更新 _count 计数器。

将 pos 位设置为 1 的方式即让 pos 位与对应的 1(其它位均为 0)相或,这样无论当前位是 0 还是 1 都可以将其变成 1 或维持 1 不变。

bool set(size_t pos)
{
    if (pos >= N)
    {
        return false;
    }

    if(!test(pos))
    {
   		_v[pos / 8] |= 1 << (pos % 8);
        ++_count;
    }

    return true;
}

reset

reset 的作用与 set 相反,需要将对应位设置为 0。同样,已经为 0 时不需要更新 _count 计数器。

将 pos 位设置为 0 的方式即让 pos 位与对应的 0(其它位均为 1)相与,这样无论 pos 位是 0 还是 1都可以将其变为 0 或维持 0 不变。

bool reset(size_t pos)
{
    if (pos >= N)
    {
        return false;
    }
    
    if(test(pos))
    {
        _v[pos / 8] &= ~(1 << (pos % 8));
        --_count;
	}

    return true;
}

count

返回有多少个比特位被 set 了。

int count()
{
    return _count;
}

operator[]

下标引用操作符的作用和 test 一致,可以认为仅仅是对 test 的一层封装。

bool operator[](int pos)
{
    return test(pos);
}

整体实现

#include <iostream>

#include <vector>

namespace Thepale
{
	template <size_t N = 32>
	class bitset
	{
	public:
		bitset()
		{
			_v.resize(N / 8 + 1, 0);
		}

		bool test(size_t pos)
		{
			assert(pos < N);

			return _v[pos / 8] & (1 << (pos % 8));
		}

		bool set(size_t pos)
		{
			if (pos >= N)
			{
				return false;
			}

			if (!test(pos))
			{
				_v[pos / 8] |= 1 << (pos % 8);
				++_count;
			}

			return true;
		}

		bool reset(size_t pos)
		{
			if (pos >= N)
			{
				return false;
			}

			if (test(pos))
			{
				_v[pos / 8] &= ~(1 << (pos % 8));
				--_count;
			}

			return true;
		}

		int count()
		{
			return _count;
		}

		bool operator[](int pos)
		{
			return test(pos);
		}

	private:
		std::vector<char> _v;
		int _count;
	};
}