题目: 1-1000放在含有1001个元素的数组中, 只有唯一的一个元素值重复,其它均只出现一次. 每个数组元素只能访问一次,不能使用辅助空间
解题关键
- 这道题的坑有两个
- 首先 是 每个数组元素只能访问一次
- 其次 是 不能使用辅助空间
解题思路
- 使用 异或 ^ 运算 符号 ( 类似加密算法 )
- A ^ A = 0 和 B ^ 0 = B
- 通过上面两个公式 我们可以想到一个解题思路
- [1~1000] ^ [1~1000 + 重复的数字] 等于 A ^ A = 0 所有数字都会和再次出现的 数字 异或之后变成 0 而只有多出来的数字没有变成0
解题代码
import java.util.Random;
/*
* 1-1000放在含有1001个元素的数组中,
* 只有唯一的一个元素值重复,其它均只出现一次.
* 每个数组元素只能访问一次,
* 辅助空间的使用
* */
public class 唯一成对数1 {
/*
* 此方法 遍历 整形数组
*/
public static void showArr(int [] arr){
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
/*
* 初始化数组 定义数组的大小
* 先定义 为 int [11] 10+1 1是相同的数字
* 数据量较小 方便计算
*/
// 如果下面的代码运行无误 把 11 改成 1001 即可
int len = 11;
int [] arr = new int [len];
// 数组的数据 应该是 1~10 加上一个随机数(1~10)范围之内
for (int i = 0; i < len-1;i++){
arr[i] = i+1; //1~10
}
/*
* 需要一个随机数(1~10)范围之内
* 此函数会返回一个 包括0 ~ 不包括 括号里参数的数字
* 返回 包括0+1 ~ 不包括 11-1+1 的数字
*/
int random = new Random().nextInt(len-1)+1;
arr[arr.length-1] = random;
//在这里展示一下自己构建的数组
showArr(arr);
/*
* 上面的构造 整型数组 的方法不唯一
* 还能花里胡哨一点就是 随机生成一个下标 与最后一个成对数字交换 这样 数组 最后一个就不是成对数了
* 数组不是关键 如何解题才是
*/
/*
* 使用 ^ 异或 运算符
* 原理
* A ^ A = 0
* B ^ 0 = B
* 先使用 最初的数组 1 ~ 10 只要它们与0 异或 得到结果 这里可以理解为 加密 0^1^2^3^4^...10
* 然后使用 有成对数 出现的数组 来 解密 加密的结果 ^ 成对数数组
* 由于有一个多出现的数字 没有参与 异或 运算 最后这有他 无法通过 A ^ A = 0 来变成0 被剩下来
*/
int code = 0;
// 加密
for(int i = 1; i < 11; i++){
code = code ^ i;
}
// 循环结束 现在的 code 是 加密的结果
//解密
for(int i =0;i < arr.length; i++){
code = code ^ arr[i];
}
// 循环结束 现在的 code 是 解密的结果
// 由于 我们控制 数组最后一位是 成对数 两者 只要比较 code 是否等于 arr[最后一位]
System.out.println("======");
System.out.println(code);
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间 "+(endTime - beginTime)+" 毫秒");
}
}
运行结果
1 2 3 4 5 6 7 8 9 10 2
======
2
程序运行时间 0 毫秒
解题优化
不用看了 这是最快的了 位运算是最快的运算 java计算的底层原理就是依靠二进制实现的
System.out.println("========================================================");
如果上面的解题思路没有明白 下面这种是比较简单的 思路
解题思路
使用一个新的数组存放 目标数组中元素 出现的次数(把目标元素作为新数组的下标) 当新定义的数组中有个元素为2是 新定义的数组值为2 的下标就是 目标数组中 成对出现的数字
注意!!!这个方法会违反 不能使用辅助空间的规则 不过不用担心 等等还有一个方法 两个规则都会违反
解题代码
import java.util.Random;
public class 唯一成对数2 {
/*
* 此方法 遍历 整形数组
*/
public static void showArr(int [] arr){
for (int i = 0; i < arr.length; i++){
System.out.print(arr[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
/* 解题思路
* 使用一个新的数组来存放 目标数组中出现的元素的次数
* 如果一个数字出现的次数 超过2
* 那它就会结果
*/
// 如果下面的代码运行无误 把 11 改成 1001 即可
int len = 11;
int [] arr = new int [len];
// 数组的数据 应该是 1~10 加上一个随机数(1~10)范围之内
for (int i = 0; i < len-1;i++){
arr[i] = i+1; //1~10
}
/*
* 需要一个随机数(1~10)范围之内
* 此函数会返回一个 包括0 ~ 不包括 括号里参数的数字
* 返回 包括0+1 ~ 不包括 11-1+1 的数字
*/
int random = new Random().nextInt(len-1)+1;
arr[arr.length-1] = random;
//在这里展示一下自己构建的数组
showArr(arr);
/* 注意!!!初始化一个整型数组 数组里面的数字为0
* 定义一个新的数组 0号下标不看 因为用不到 目标数组是从 1开始的
* 把目标元素出现的 数字 作为 新数组的下标
*/
int [] count = new int [len];
for (int i = 0; i<arr.length;i++){
count[arr[i]]++;
}
//如果 新数组中的有个数字为2 那就是 结果
for (int i =0;i<count.length;i++){
if(count[i] == 2){
System.out.println("===============");
System.out.println("成对出现的数字是 "+i);
}
}
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间 "+(endTime - beginTime)+" 毫秒");
}
}
运行结果
1 2 3 4 5 6 7 8 9 10 3
===============
成对出现的数字是 3
程序运行时间 1 毫秒
解题优化
这个方式把 目标数组的值 作为 新数组的 下标 当下标两次出现 即可求得 成对出现的数字 不过在数据量庞大的时候 效率没有第一种方法效率高 数据量越大 效率差距越是显著
System.out.println("========================================================");
如果上面两种方法都没有明白 下面还有一种终极方法
解题思路
已知的是 两个数组 一个数组是 1~1000 没有成对出现的数字 我们做累加 所有的数字都加起来
另外一个数组 是 1~1000 + 一个前面1000个的数字中的一个 我们做累加 所有的数字都加起来
最后两个和相减 得到的数字就是 多出来数字
注意!!!这个方法两项规则都违反了 当你考试的时候做不来的时候 可以写出来自己开心一下
解题代码
public class 唯一成对数3 {
public static void show(int[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int N = 11;
int[] arr = new int[N];
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = i + 1;
}
show(arr);
int sum=0;
for(int i =0;i<arr.length;i++){
sum +=arr[i];
}
System.out.println("sum="+sum);
arr[arr.length - 1] = new Random().nextInt(N - 1);
show(arr);
int s = 0;
for(int i =0;i<arr.length;i++){
s +=arr[i];
}
System.out.println("sum="+s);
System.out.println("多出来的数="+(s-sum));
}
}
运行结果
1 2 3 4 5 6 7 8 9 10 0
sum=55
1 2 3 4 5 6 7 8 9 10 3
sum=58
多出来的数=3
解题优化
自己写的挺开心的 就是不得分