蓝桥杯:Java实现在数组中查找唯一成对数 三种实现方法

题目: 1-1000放在含有1001个元素的数组中, 只有唯一的一个元素值重复,其它均只出现一次. 每个数组元素只能访问一次,不能使用辅助空间

 

解题关键

  1. 这道题的坑有两个
  2. 首先 是 每个数组元素只能访问一次
  3. 其次 是 不能使用辅助空间 

 解题思路

  1. 使用 异或 ^ 运算 符号 ( 类似加密算法 )
  2. A ^ A = 0    和  B ^ 0 = B
  3. 通过上面两个公式 我们可以想到一个解题思路 
  4. [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

解题优化

自己写的挺开心的 就是不得分

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值