Set就是集合类型,最常见用操作就是判有判无。
常见的底层实现有位向量实现和有序链表实现。
有序链表很简单,双指针撸到底,这里不详细讲。下面我们谈谈java实现位向量。
不止java,C++中也没有直接的位操作。所以想把整数类型映射到一个bit位上,我们要自己定义操作。
C++比较方便的是有unsigned类型。在unsigned 上左移右移都补0。
可java没有unsigned类型。这就麻烦了。如果我们定义short[] 作为位向量 是肯定会出现错误的。
虽然java有额外的>>>移位,右移始终补0。但对于short,它会先将short转为int,再移位,最后再截短赋值。这就会出现错误。
由于short 16bit, int 32bit,演示过于麻烦。我们将其截半,结果是一样的。
比如-1, 8bit 表示 1111 1111 如果>>>2 移位, 我们期望的结果是 0011 1111。
但实际上 -1 会先转为16位: 1111 1111 1111 1111, 再移位: 0011 1111 1111 1111, 最后截短赋值: 1111 1111。
看,本来我们期望结果是0011 1111,但实际上却是1111 1111。这对于我们实现值位映射是不利的。
所以用java实现位向量,不可用short[],直接用int[],不可用算术右移>>, 直接用逻辑右移>>>。这样就没问题了。
这样的话,一个int可以存放32个元素的有无信息,充分利用了存储空间。
看,java也可以如此底层。
至于集合的交,并,差等,可以用位运算& | ~ 等实现。最最核心的代码时实现值位存取映射。
下面给出代码:
public class BitSet { private int setSize; // setSize is the element number of set private int vectorSize; // vectorSize is the storage that needed by the set private int[] bitVector; public BitSet(){ this.setSize = 1000; this.vectorSize = (this.setSize + 31) >> 5; this.bitVector = new int[this.vectorSize]; } /** * set the volume of the set * @param setSize the volume of the set */ public BitSet(int setSize){ this.setSize = setSize; this.vectorSize = (setSize + 31) >> 5; this.bitVector = new int[this.vectorSize]; } /** * set member value.Note that the min member should be 1, not 0. * @param element the element of set * @param value the value to set; only 0 or 1 * @return true or false */ public boolean setMemberValue(int element, int value){ if (element < 1 || element > setSize){ System.err.println("Out of Range!"); return false; } element -= 1; // begin with 0 instead of 1. int id = element / 32; int ad = element % 32; int tmp = bitVector[id]; int last = tmp >>> (31 - ad); tmp = tmp << (ad + 1); if (last % 2 == 0 && value == 1){ last = last + 1; } if (last % 2 == 1 && value == 0){ last -= 1; } bitVector[id] = (last << (31 - ad)) | (tmp >>> (ad + 1)); return true; } /** * get the value of the element. Note that the min element is 1, not 0; * @param element * @return if the element in the set, return 1; else 0. */ public int getMemberValue(int element){ if (element < 1 || element > setSize){ System.err.println("Out Of Range!"); return -1; } element -= 1; int id = element / 32; int ad = element % 32; int result = bitVector[id] >>> (31-ad); result %= 2; return result; } /** * add the element to the set * @param element * @return */ public boolean addMember(int element){ if (getMemberValue(element) == 1){ System.err.println("Already In!"); return false; } return setMemberValue(element,1); } /** * delete the element of the set * @param element * @return */ public boolean delMember(int element){ if (getMemberValue(element) == 0){ System.err.println("Not have it!"); return false; } return setMemberValue(element,0); } /** * print an int in binary string, from the first bit that not 0. * @param value */ public void printBinaryValue(int value){ System.out.println(Integer.toBinaryString(value)); } public static void main(String[] args) { // write your code here BitSet bitSet = new BitSet(5000); bitSet.addMember(1); bitSet.addMember(31); bitSet.addMember(20); bitSet.addMember(32); bitSet.addMember(2); bitSet.printBinaryValue(bitSet.bitVector[0]); bitSet.addMember(21); bitSet.printBinaryValue(bitSet.bitVector[0]); bitSet.delMember(21); bitSet.printBinaryValue(bitSet.bitVector[0]); System.out.println(bitSet.getMemberValue(1)); System.out.println(bitSet.getMemberValue(20)); } }