Same binary weight 位操作 bitset用法 STL

本文介绍了一种高效算法,用于找到大于给定整数N的最小整数,该整数与N具有相同的二进制权重(即1的数量相同)。通过直接操作二进制位来实现,避免了使用BitSet等数据结构导致的时间效率问题。

Same binary weight

时间限制: 300 ms  |  内存限制: 65535 KB
难度: 3
描述

The binary weight of a positive  integer is the number of 1's in its binary representation.for example,the decmial number 1 has a binary weight of 1,and the decimal number 1717 (which is 11010110101 in binary) has a binary weight of 7.Give a positive integer N,return the smallest integer greater than N that has the same binary weight as N.N will be between 1 and 1000000000,inclusive,the result is guaranteed to fit in a signed 32-bit interget.

输入
The input has multicases and each case contains a integer N.
输出
For each case,output the smallest integer greater than N that has the same binary weight as N.
样例输入
1717
4
7
12
555555
样例输出
1718
8
11
17
555557
来源
topcoder
上传者
骆魁永
大意是找出n转化为2进制时有相同的1,离得最近的那个;
直接用BitSet超时:
#include<iostream>
#include<bitset>
using namespace std;
int main()
{
	int n;
	while(cin>>n)
	{
		bitset<32> b(n);//将n转化为二进制
		int temp1=b.count(),temp2;
		for(int i=n+1;;++i)
		{
			bitset<32> c(i);
			temp2=c.count();//统计1的个数
			if(temp1==temp2)
			{
				cout<<i<<endl;
				break;
			}
		}
	}
}

先看看这几个例子:

1717(0110 1011 0101),下一位是 1718(0110 1011 0110

767(0010 1111 1111),下一位是 895(0011 0111 1111)

348(0001 0101 1100),下一位是 355(0001 0110 0011

其中不难发现一个规律,从右起的第一个“01”改变为“10”,并且在“01”的后面所有的“1”都移动至最后,事实上,这个就是解题的关键点,那么整个问题求解的核心就转移到这两个子问题:

1. 将右起第一个“01”,改变为“10”

2. 将该“01”后面的所有“1”移动至最后

所以直接操作二进制位就ok;
#include<iostream>
#include<bitset>
using namespace std;
int main()
{
	int n;
	while(cin>>n)
	{
		bitset<32> b(n);
		//cout<<b<<endl;
		//for(int k=0;k<32;++k)
		//cout<<b[k]; 
		//cout<<endl;
		int i,cont=0;
		for(i=0;i<32;i++)
		{
			if(b[i]==1)
				cont++;
			if(b[i]==1&&b[i+1]==0)
			{
				bool t;
				t=b[i];
				b[i]=b[i+1];
				b[i+1]=t;
				break;
			}
		}
		int j=i-1;
		for(i=0;i<=j;i++)
		{
			if(i<cont-1)
				b[i]=1;
			else
				b[i]=0;
		}
		//cout<<b<<endl;
		cout<<b.to_ulong()<<endl; 

	}
}


<think>在Java中,`org.bson.types.Binary` 和 `java.util.BitSet` 是两种不同的二进制数据表示方式,但它们可以互相转换。`Binary` 是MongoDB驱动中用于表示二进制数据的类,而`BitSet`是Java标准库中用于位操作的类。 ### 转换方案 #### 1. 从 `BitSet` 转换为 `Binary` ```java import org.bson.types.Binary; import java.util.BitSet; public class BitmapConverter { // 将BitSet转换为Binary public static Binary bitSetToBinary(BitSet bitSet, int numBits) { // 计算所需字节数 int numBytes = (numBits + 7) / 8; byte[] bytes = new byte[numBytes]; // 将BitSet转换为字节数组 for (int i = 0; i < numBits; i++) { if (bitSet.get(i)) { int byteIndex = i / 8; int bitOffset = i % 8; bytes[byteIndex] |= (1 << bitOffset); } } return new Binary(bytes); } } ``` #### 2. 从 `Binary` 转换为 `BitSet` ```java import org.bson.types.Binary; import java.util.BitSet; public class BitmapConverter { // 将Binary转换为BitSet public static BitSet binaryToBitSet(Binary binary, int numBits) { byte[] bytes = binary.getData(); BitSet bitSet = new BitSet(numBits); for (int i = 0; i < numBits; i++) { int byteIndex = i / 8; if (byteIndex >= bytes.length) { break; // 超出数据范围 } int bitOffset = i % 8; boolean isSet = ((bytes[byteIndex] >> bitOffset) & 1) == 1; if (isSet) { bitSet.set(i); } } return bitSet; } } ``` ### 使用示例 ```java // 创建包含10个位的BitSet int numBits = 10; BitSet originalBitSet = new BitSet(numBits); originalBitSet.set(2); // 设置第3位 originalBitSet.set(5); // 设置第6位 // 转换为Binary Binary binary = BitmapConverter.bitSetToBinary(originalBitSet, numBits); // 存储到MongoDB Document doc = new Document("bitmap", binary); collection.insertOne(doc); // 从MongoDB读取 Document retrieved = collection.find().first(); Binary retrievedBinary = retrieved.get("bitmap", Binary.class); // 转换回BitSet BitSet convertedBitSet = BitmapConverter.binaryToBitSet(retrievedBinary, numBits); // 验证 System.out.println(convertedBitSet.get(2)); // 输出 true System.out.println(convertedBitSet.get(5)); // 输出 true System.out.println(convertedBitSet.get(0)); // 输出 false ``` ### 关键注意事项 1. **位顺序问题**: - Java的`BitSet`内部使用小端序存储(最低位在索引0) - 我们的转换方法采用**逻辑位索引顺序**(与BitSet一致) - 示例:字节`00000100`(二进制)表示第3位(索引2)被设置 2. **大小处理**: - 必须指定总位数(`numBits`),因为: - `BitSet` 自动扩展大小 - `Binary` 的字节数组可能包含尾部未用位 - 转换时截断到指定`numBits` 3. **性能考虑**: - 时间复杂度:O(n)(n为位数) - 空间复杂度:O(n/8)(字节数组) - 适合中小型位图(<100万位),超大位图需分块处理 ### 优化技巧 #### 1. 批量操作优化 ```java // 使用BitSet.toByteArray()(注意:会忽略尾部0) public static Binary bitSetToBinaryFast(BitSet bitSet, int numBits) { // 计算所需字节数(考虑尾部0) int numBytes = (numBits + 7) / 8; byte[] bytes = bitSet.toByteArray(); // 如果bitSet.toByteArray()返回的数组小于所需,则扩展 if (bytes.length < numBytes) { bytes = Arrays.copyOf(bytes, numBytes); } return new Binary(bytes); } ``` #### 2. 使用`ByteBuffer`处理大位图 ```java // 处理1亿位的大位图(约12MB) public static Binary largeBitSetToBinary(BitSet bitSet, int numBits) { int numBytes = (numBits + 7) / 8; ByteBuffer buffer = ByteBuffer.allocate(numBytes); // 每次处理8位(1字节) for (int i = 0; i < numBits; i += 8) { byte b = 0; int end = Math.min(i + 8, numBits); for (int j = i; j < end; j++) { if (bitSet.get(j)) { b |= (1 << (j - i)); } } buffer.put(b); } return new Binary(buffer.array()); } ``` ### 常见问题解决 **问题:转换后位顺序错乱?** 原因:不同系统/库的位顺序定义不同 解决方案:统一使用**最低有效位(LSB)优先**标准 ```java // 修正位偏移计算(确保LSB优先) bytes[byteIndex] |= (1 << (7 - bitOffset)); // 改为大端序?不,我们之前使用的小端序 // 注意:之前的转换方法使用的是小端序(最低位对应索引0),与BitSet一致 // 如果与其他系统(如C++)交互时需确认字节序 ``` **问题:尾部未用位处理?** 在转换回`BitSet`时,通过指定`numBits`忽略多余位: ```java // 在binaryToBitSet方法中 if (i >= numBits) break; ``` ### 何时使用`BitSet`? | 场景 | 推荐方案 | |------|----------| | **需要复杂位操作**(如AND/OR运算) | 使用`BitSet`转换后操作 | | **仅存储和简单查询** | 直接使用`Binary`+工具类 | | **超大位图(>1GB)** | 自定义分片+文件存储 | §§相关问题§§: 1. 如何解决不同系统间(如Java和C++)位图的字节序差异? 2. 使用`BitSet`与直接操作字节数组在性能上有何差异? 3. 有哪些开源库可以优化位图操作? 4. 在分布式系统中如何同步位图状态?</think>是的,**`org.bson.types.Binary` 和 `java.util.BitSet` 可以互相转换**。以下是完整的转换方案和代码实现: ### 转换原理 1. **内存结构**: - `Binary`:存储原始字节数组 (`byte[]`) - `BitSet`:内部使用 `long[]` 存储位数据 2. **转换关键**: - 将 `BitSet` 的位序列转换为字节数组 - 将字节数组解析为位序列 ### 完整转换工具类 ```java import org.bson.types.Binary; import java.util.BitSet; import java.util.Arrays; public class BitmapConverter { // 将Binary转换为BitSet public static BitSet binaryToBitSet(Binary binary, int numBits) { if (binary == null) return new BitSet(); byte[] bytes = binary.getData(); BitSet bitSet = new BitSet(numBits); // 遍历每个字节 for (int byteIndex = 0; byteIndex < bytes.length; byteIndex++) { byte b = bytes[byteIndex]; // 遍历字节中的每个位 for (int bitOffset = 0; bitOffset < 8; bitOffset++) { int globalIndex = byteIndex * 8 + bitOffset; if (globalIndex >= numBits) break; // 超出指定位数 // 检查位是否设置 (小端序:最低位是索引0) if (((b >> bitOffset) & 1) == 1) { bitSet.set(globalIndex); } } } return bitSet; } // 将BitSet转换为Binary public static Binary bitSetToBinary(BitSet bitSet, int numBits) { if (bitSet == null) return new Binary(new byte[0]); // 计算所需字节数 (向上取整) int numBytes = (numBits + 7) / 8; byte[] bytes = new byte[numBytes]; // 遍历所有位 for (int i = 0; i < numBits; i++) { if (bitSet.get(i)) { int byteIndex = i / 8; int bitOffset = i % 8; // 设置对应位 (小端序:最低位是索引0) bytes[byteIndex] |= (1 << bitOffset); } } return new Binary(bytes); } } ``` ### 使用示例 ```java import org.bson.types.Binary; import java.util.BitSet; public class Main { public static void main(String[] args) { // 1. 创建BitSet并设置位 BitSet originalBitSet = new BitSet(24); originalBitSet.set(0); // 第1位 originalBitSet.set(3); // 第4位 originalBitSet.set(10); // 第11位 originalBitSet.set(23); // 第24位 // 2. 转换为Binary Binary binary = BitmapConverter.bitSetToBinary(originalBitSet, 24); System.out.println("Binary data: " + Arrays.toString(binary.getData())); // 输出: [1, 8, 0, 4] // 解释: // 字节0: 00000001 (位0设置) // 字节1: 00001000 (位3设置) // 字节2: 00000000 (无位设置) // 字节3: 00000100 (位23设置,因为23-16=7? 注意索引计算) // 3. 转换回BitSet BitSet convertedBitSet = BitmapConverter.binaryToBitSet(binary, 24); // 4. 验证转换结果 System.out.println("位0: " + convertedBitSet.get(0)); // true System.out.println("位3: " + convertedBitSet.get(3)); // true System.out.println("位10: " + convertedBitSet.get(10)); // true System.out.println("位23: " + convertedBitSet.get(23)); // true System.out.println("位1: " + convertedBitSet.get(1)); // false } } ``` ### 关键细节说明 1. **位序问题**: - Java 使用**小端序(Little-Endian)** 位索引: - 字节 `00000001` 表示位0被设置 - 字节 `00001000` 表示位3被设置 - 这与大多数硬件处理位的方式一致 2. **大小处理**: - 必须指定总位数 (`numBits`),因为: - `BitSet` 会自动扩展大小 - `Binary` 的字节数组是固定大小的 - 转换时截断到指定 `numBits` 3. **性能优化**: ```java // 批量操作优化 public static Binary bitSetToBinaryOptimized(BitSet bitSet, int numBits) { int numBytes = (numBits + 7) / 8; byte[] bytes = new byte[numBytes]; // 使用nextSetBit减少迭代次数 for (int i = bitSet.nextSetBit(0); i >= 0; i = bitSet.nextSetBit(i+1)) { if (i >= numBits) break; int byteIndex = i >>> 3; // 等价于 i/8 int bitOffset = i & 0x7; // 等价于 i%8 bytes[byteIndex] |= (1 << bitOffset); } return new Binary(bytes); } ``` ### 存储优化建议 1. **压缩稀疏位图**: ```java // 使用行程编码压缩 public byte[] compressBitSet(BitSet bitSet) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int count = 0; boolean current = false; for (int i = 0; i <= bitSet.size(); i++) { if (i < bitSet.size() && bitSet.get(i) == current) { count++; } else { while (count > 0) { int chunk = Math.min(count, 255); baos.write(current ? chunk : -chunk); count -= chunk; } current = !current; count = 1; } } return baos.toByteArray(); } ``` 2. **使用RoaringBitmap**: 对于大型位图,推荐使用优化的位图库: ```java // 添加Maven依赖 // <dependency> // <groupId>org.roaringbitmap</groupId> // <artifactId>RoaringBitmap</artifactId> // <version>0.9.38</version> // </dependency> import org.roaringbitmap.RoaringBitmap; // 转换为RoaringBitmap public static Binary toRoaringBinary(BitSet bitSet) { RoaringBitmap rBitmap = new RoaringBitmap(); rBitmap.or(bitSet); // 高效转换 byte[] bytes = rBitmap.serialize(); return new Binary(bytes); } ``` ### 常见问题解决 **问题:转换后位索引错位?** **解决方案**:统一位序处理 ```java // 如果需大端序(Big-Endian)处理 bytes[byteIndex] |= (1 << (7 - bitOffset)); ``` **问题:内存占用过大?** **解决方案**:分块处理 ```java // 分块处理10万位/块 public List<Binary> chunkedConversion(BitSet largeBitSet, int chunkSize) { List<Binary> chunks = new ArrayList<>(); for (int i = 0; i < largeBitSet.size(); i += chunkSize) { BitSet chunk = largeBitSet.get(i, Math.min(i + chunkSize, largeBitSet.size())); chunks.add(bitSetToBinary(chunk, chunkSize)); } return chunks; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值