问题描述:
给定一个数组arr,已知除了一种数只出现1次之外,剩下所有的数都出现了k次,如何使用O(1)的额外空间,找到这个数。
思路:
我们使用二进制的改进思想进行解题。
数组中的数字出现k次,就是用k进制进行解题。32的二进制可以表示int类型的所有数,k(k>=2)进制表示int类型的所有整数就不需要32位了。我们这里统一使用一个32大小的数组来表示k进制的数。
把arr数组中的每一个数字转为k进制数字,每一个数字每一位都叠加到新建的32大小的数组中,最后每一位都模k,得到的k进制数字转为十进制即为最后的答案。
但是上面过程中最后求每一位上的模存在一个问题,若数据量过大可能会有越界的问题,所以我们可以在每次对相应位进行加法操作是就进行模k操作,就可以避免越界问题的产生。
代码:
public static int onceNum(int[] arr, int k) {
int[] e0 = new int[32];
for (int i = 0; i != arr.length; i++) {
setExclusiveOr(e0, arr[i], k);
}
int res = getNumFromKSysNum(e0, k);
return res;
}
public static void setExclusiveOr(int[] e0, int value, int k) {
int[] curKSysNum = getKSysNumFromNum(value, k);
for (int i = 0; i != e0.length; i++) {
e0[i] = (e0[i] + curKSysNum[i]) % k;
}
}
public static int[] getKSysNumFromNum(int value, int k) {
int[] res = new int[32];
int index = 0;
while (value != 0) {
res[index++] = value % k;
value = value / k;
}
return res;
}
public static int getNumFromKSysNum(int[] e0, int k) {
int res = 0;
for (int i = e0.length - 1; i != -1; i--) {
res = res * k + e0[i];
}
return res;
}
public static void main(String[] args) {
int[] test1 = {1, 1, 1, 2, 6, 6, 2, 2, 10, 10, 10, 12, 12, 12, 6, 9};
System.out.println(onceNum(test1, 3));
int[] test2 = {-1, -1, -1, -1, -1, 2, 2, 2, 4, 2, 2};
System.out.println(onceNum(test2, 5));
}