位图

位图

位图原理

其实就是用bit组成的数组来存放值,用bit状态1、0代表存在、不存在,取值和存值操作都用位运算

限制是必须为连续范围不能过大。好处是极大的节省空间,因为1个数字只占用1个bit的空间。

位图的实现

  • Bitset(int n):初始化位图的大小,只支持0~n-1所有数字的增删改查
  • void add(int num):把num加入到位图
  • void remove(int num):把num从位图中删除
  • void reverse(int num):如果位图里没有num,就加入;如果位图里有num,就删除
  • boolean contains(int num):查询num是否在位图中
	// 位图的实现
	// 使用时num不要超过初始化的大小
	public static class Bitset {
		public int[] set;

		// n个数字 : 0~n-1
		public Bitset(int n) {
			// a/b如果结果想向上取整,可以写成 : (a+b-1)/b
			// 前提是a和b都是非负数
			set = new int[(n + 31) / 32];
			// 0~31:一个int 32~63:一个int
		}

		public void add(int num) {// 用1或进去
			set[num / 32] |= 1 << (num % 32);// 1来到num所在位置上,把1或进去
		}

		public void remove(int num) {// 用0消除
			set[num / 32] &= ~(1 << (num % 32));// 1101 1111
		}

		public void reverse(int num) {// 在/不在->不在/在: 与1异或
			set[num / 32] ^= 1 << (num % 32);
		}

		public boolean contains(int num) {// 看这一位是1是0: 提取该位
			return ( ( set[num / 32] >> (num % 32) ) & 1) == 1;
		}

	}
  • 对数器测试
	public static void main(String[] args) {
		int n = 1000;// 0~999这些数字
		int testTimes = 10000;// 一万次
		System.out.println("测试开始");
		// 实现的位图结构
		Bitset bitSet = new Bitset(n);
		// 直接用HashSet做对比测试
		HashSet<Integer> hashSet = new HashSet<>();
		System.out.println("调用阶段开始");
		for (int i = 0; i < testTimes; i++) {
			double decide = Math.random();
			// number -> 0 ~ n-1,等概率得到
			int number = (int) (Math.random() * n);
			if (decide < 0.333) {
				bitSet.add(number);
				hashSet.add(number);
			} else if (decide < 0.666) {
				bitSet.remove(number);
				hashSet.remove(number);
			} else {
				bitSet.reverse(number);
				if (hashSet.contains(number)) {
					hashSet.remove(number);
				} else {
					hashSet.add(number);
				}
			}
		}
		System.out.println("调用阶段结束");
		System.out.println("验证阶段开始");
		for (int i = 0; i < n; i++) {
			if (bitSet.contains(i) != hashSet.contains(i)) {
				System.out.println("出错了!");
			}
		}
		System.out.println("验证阶段结束");
		System.out.println("测试结束");
	}

测试链接

https://leetcode-cn.com/problems/design-bitset/

  • 位图的实现

    • Bitset是一种能以紧凑形式存储位的数据结构
    • Bitset(int n) : 初始化n个位,所有位都是0
    • void fix(int i) : 将下标i的位上的值更新为1
    • void unfix(int i) : 将下标i的位上的值更新为0
    • void flip() : 翻转所有位的值
    • boolean all() : 是否所有位都是1
    • boolean one() : 是否至少有一位是1
    • int count() : 返回所有位中1的数量
    • String toString() : 返回所有位的状态
  • 思路

    • 用变量标识0,1,能够表示的数的个数 用reverse表示是否经过反转
    • 每一次反转都将ones zeros交换,revserse取反
    • 在进行插入,删除操作时, 要根据是否反转写出不同的操作
  • 代码

    • 	class Bitset {
      		private int[] set;
      		private final int size;// 一共要支持多大的范围
      		private int zeros;// 0的数量
      		private int ones;// 1的数量
      		private boolean reverse;// 是否反转
      
      		public Bitset(int n) {// 初始化
      			set = new int[(n + 31) / 32];// 分配空间
      			size = n;// 大小
      			zeros = n;
      			ones = 0;
      			reverse = false;// 标志
      		}
      
      		// 把i这个数字加入到位图
      		public void fix(int i) {
      			int index = i / 32;// 属于哪个整数
      			int bit = i % 32;// 属于该整数的第几位
      			if (!reverse) {
      				// 位图所有位的状态,维持原始含义
      				// 0 : 不存在
      				// 1 : 存在
      				if ((set[index] & (1 << bit)) == 0) {// 如果i这个数原本不存在位图中,加入i
      					zeros--;
      					ones++;
      					set[index] |= (1 << bit);
      				}
      			} else {
      				// 位图所有位的状态,翻转了
      				// 0 : 存在
      				// 1 : 不存在
      				if ((set[index] & (1 << bit)) != 0) {
      					zeros--;// 个数的标识不变
      					ones++;
      					set[index] ^= (1 << bit);// 0,1所代表的状态改变
      				}
      			}
      		}
      
      		// 把i这个数字从位图中移除
      		public void unfix(int i) {
      			int index = i / 32;
      			int bit = i % 32;
      			if (!reverse) {
      				if ((set[index] & (1 << bit)) != 0) {// 存在
      					ones--;
      					zeros++;
      					set[index] ^= (1 << bit);// 反转
      				}
      			} else {
      				if ((set[index] & (1 << bit)) == 0) {
      					ones--;
      					zeros++;
      					set[index] |= (1 << bit);
      				}
      			}
      		}
      
      		public void flip() {
      			reverse = !reverse;
      			int tmp = zeros;
      			zeros = ones;
      			ones = tmp;
      		}
      
      		public boolean all() {
      			return ones == size;
      		}
      
      		public boolean one() {
      			return ones > 0;
      		}
      
      		public int count() {
      			return ones;
      		}
      
      		public String toString() {
      			StringBuilder builder = new StringBuilder();
      			for (int i = 0, k = 0, number, status; i < size; k++) {// i用以判断最后一个int中的状态是否到达size
      				number = set[k];// 取出每一个整数
      				for (int j = 0; j < 32 && i < size; j++, i++) {// 每个数的每一个状态
      					status = (number >> j) & 1;// 取出每一个数
      					status ^= reverse ? 1 : 0;// 看看是否被反转过
      					builder.append(status);
      				}
      			}
      			return builder.toString();
      		}
      
      	}
      
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值