以前所接触到的位图的思想都是以1位的形式去存储某个数出现的次数是1次还是0次。常见的例子不外乎在《编程珠玑》上的开篇例子里,1千万个数的排序统计,用1.25M的内存空间就可以达到遍历一遍输入数据而排序好的目的。这种思想是通用的么?也就是说,假如输入数据不再是0次或者1次,而是2次或者更多的时候,如何再次用上这种思想呢?请看下面题目 位图是一种高效的数据结构,常用于处理有限且连续的整数集合。在传统的位图思想中,我们通常用一个二进制位来表示一个特定数值是否出现,1表示出现,0表示未出现。例如,在《编程珠玑》的经典例子中,通过位图可以快速统计1千万个整数的出现次数,只需1.25MB的内存,因为2^20(1M)个二进制位足以表示0到1000万之间的整数。 然而,当遇到更复杂的场景,如题目所述,数组中的元素可能不止出现一次或两次,而是可能出现多次。在这种情况下,我们仍能利用位图的思想来解决问题,但需要进行一些调整。题目要求找出一个数组中仅出现一次或两次的整数,其余所有数字都出现三次。 解法一尝试用两个二进制位来记录每个数的出现次数。例如,我们可以用00表示未出现,01表示出现一次,10表示出现两次。这种方法在处理一定规模的数据时有效,但随着数据量的增加,所需内存也会迅速增长。例如,处理20亿个数时,需要250MB的内存,这在处理大规模数据时是不可接受的。 解法二是一种改进的位图策略,称为“位移累加”法。这里,我们使用32个整数数组(每个整数代表32位二进制位)来记录0到31位的出现次数。对于数组中的每个元素,我们逐位进行统计,将每个元素的第i位右移i位后与1进行按位与操作,然后累加到对应的位数计数器中。这样,如果某个数的某一位出现了两次,其计数器除以3的余数将会是2。遍历32次后,我们将这些计数器进行按位或运算,得到的结果就是我们要找的数字。这种方法的空间复杂度为32 * 32位,即1KB,远小于解法一,并且时间复杂度依然满足O(N),因为需要遍历输入数据32次。 以下是解法二的Java实现: ```java 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(N)的时间复杂度内找到仅出现一次或两次的整数,同时保持较低的空间复杂度。这对于处理大规模数据至关重要,尤其是在资源有限的环境中。
- 粉丝: 2
- 资源: 911
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助