前言:
以前所接触到的位图的思想都是以1位的形式去存储某个数出现的次数是1次还是0次。常见的例子不外乎在《编程珠玑》上的开篇例子里,1千万个数的排序统计,用1.25M的内存空间就可以达到遍历一遍输入数据而排序好的目的。这种思想是通用的么?也就是说,假如输入数据不再是0次或者1次,而是2次或者更多的时候,如何再次用上这种思想呢?请看下面题目
题目:
输入一个数组,数组有int类型整数若干,若有其中一个是出现一次或者两次,其他数字都是出现3次,要求在时间复杂度在O(N)上限里求出那个数字。
解法一
生搬硬套位图的思想,既然最多出现3次,那么我用两个bit位来存储一个数出现的个数。那么假如输入是2千万个数的时候,所需要的空间是2.5M。20亿个数的时候,需要的是250M。显然,这种思想还是不太好,虽然符合题目要求。像这样处理海量数据肯定是要被毙掉的。可以尝试着做改进,改进思路是分批处理,如面对20亿的数据,我只自定义一个1千万数的空间,2.5M,然后每次只是处理1千万的数,如果没有出现目标数,那么再处理下一个一千万的数(按照大小区分范围)。应该也算得上一种尚可的思路。
解法二
基于位图思想的变形,我用32个数来存储所有的数中0~31位出现的个数,然后对3取余,如果是2,证明这个数的某一个位出现过2次,依次从0位推到31位。最后相或即可。
这种解法只用到了32 * 32 bit的空间,至于时间,因为需要对每个位进行统计,需要遍历输入数据32次, 32* N,可依然是符合题目的O(N)要求。
这种解法,在时间上应该是要比解法一耗时少一些。第一种解法需要遍历数据约210次,因为int类型最大值是21亿(假如不包括负数),那么就是210个1千万数了。
空间也少很多。无疑是比较好的。
public static int singleNumber(int[] A) { int[] bitNum = new int[32]; int values = 0; boolean twiceFlag = false; for(int i=0;i<32;i++){ for(int j=0;j<A.length;j++){ bitNum[i] += A[j]>>i&1; } values |= (bitNum[i]%3)<<i; if (!twiceFlag && (bitNum[i]%3)>1) { twiceFlag = true; //如果出现两次 } } if(twiceFlag)values >>= 1; return values; }
还有更好的解法么?欢迎网友提出交流,谢谢。O(∩_∩)O
以另一种位图的思想来解决一道OJ题目,布布扣,bubuko.com
原文地址:http://blog.csdn.net/linfeng24/article/details/38613049