package class02;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/**
* Code03的变型
* 有一个数组arr,其中只有一种数"没有"出现K次,其余所有的数都都出现了M次。
* 求出这个没有出现K次的数字。
* K < M,M > 1。
*/
public class Code03_KM_V02 {
public static int test(int[] arr, int k, int m) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : arr) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
int ans = 0;
for (int num : map.keySet()) {
if (map.get(num) == k) {
// ans = num;
// break;
return num;
}
}
return -1;
}
public static int[] randomArray(int maxKinds, int range, int k, int m) {
int kTimesNum = randomNum(range);//随机出一个数,作为真命天子。也就是出现了k次的那个数。
//真命天子出现的次数,times。
//0.5的概率是k次,其余的概率出现任意次。只要小于m就行。
int times = Math.random() < 0.5 ? k : ((int) (Math.random() * (m - 1)) + 1);
int numKinds = (int) (Math.random() * maxKinds) + 2;//数组中至少有两种数
//数组长度为:k * 1 + (numKinds - 1) * m
/*int[] arr = new int[k + (numKinds - 1) * m];*/
int[] arr = new int[times + (numKinds - 1) * m];
int index = 0;
/*for (; index < k; index++) {*/
for (; index < times; index++) {
arr[index] = kTimesNum;//k个出现了k次的kTimeNum
}
numKinds--;//kTimeNum这种数已经填完了,所以数字的种数要减一。
HashSet<Integer> set = new HashSet<>();
set.add(kTimesNum);//在set中添加第一个数,kTimesNum。
while (numKinds != 0) {//只要数字的种类numKinds没有减到0,就一直随机生成一个数字。并将numKinds--。
int curNum;
do {
curNum = randomNum(range);
} while (set.contains(curNum));//如果随机出的数,之前已经出现过了。重新去随机生成一个数。
set.add(curNum);
numKinds--;
// for (; index < arr.length; index++) {
// arr[index] = curNum;
// }
//一次完整的for循环走完,填m个curNum;一次完整的for循环走完,填m个curNum;知道while循环结束。
for (int i = 0; i < m; i++) {
arr[index++] = curNum;//index一直在++。走一次完整的for循环,index就加 m次。
}
}
//arr填充完毕。
//先不着急返回arr,此时的arr太整齐了。打乱顺序再返回。
//让i位置的数,随机找一个数,交换。
for (int i = 0; i < arr.length; i++) {
int j = (int) (Math.random() * arr.length);//j在[0, arr.length - 1]上,不会越界。
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
return arr;
}
//[-range, +range]。have a look。[-(range-1), +(range-1)]
public static int randomNum(int range) {
int num = ((int) (Math.random() * range) + 1) - ((int) (Math.random() * range) + 1);
return num;
}
public static void main(String[] args) {
int kinds = 30;
int range = 100;
int testTimes = 1000;
int max = 9;
System.out.println("测试开始!");
for (int i = 0; i < testTimes; i++) {
int num1 = (int) (Math.random() * max) + 1;//num1属于[1, 9]
int num2 = (int) (Math.random() * max) + 1;//num2属于[1, 9]
int k = Math.min(num1, num2);
int m = Math.max(num1, num2);
if (k == m) {//必须 k<m
m++;
}
int[] arr = randomArray(kinds, range, k, m);
int ans1 = test(arr, k, m);
int ans2 = onlyKTimes(arr, k, m);
if (ans1 != ans2) {
System.out.println(ans1);
System.out.println(ans2);
System.out.println("k = " + k);
System.out.println("m = " + m);
System.out.println("arr = " + Arrays.toString(arr));
System.out.println("oops!");
break;
}
}
System.out.println("测试结束!");
}
public static int onlyKTimes(int[] arr, int k, int m) {
//先定义一个长度位为32的整数数组,叫t。
//每一位用来标记arr中的所有num,在i位置的1出现了几个。
int[] t = new int[32];
for (int num : arr) {
for (int i = 0; i < 32; i++) {
t[i] += (num >> i) & 1;
}
}
int ans = 0;
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
for (int i = 0; i < 32; i++) {
if (t[i] % m == 0) {
continue;
}
if (t[i] % m == k) {
ans |= 1 << i;
} else {
return -1;
}
}
if (ans == 0) {
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
count++;
}
}
if (count != k) {
System.out.println("count = " + count);
System.out.println("k = " + k);
System.out.println("m = " + m);
System.out.println("arr = " + Arrays.toString(arr));
return -1;
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
return ans;
}
// public static void main(String[] args) {
// int[] arr = {6, 6, 6, 6, 7, 7, 7, 7, 3, 3, 3};//6,4次。7,4次。3,3次。
// int i = onlyKTimes(arr, 3, 4);
// System.out.println("i = " + i);
// }
}