zoukankan      html  css  js  c++  java
  • 面试题:寻找缺失的整数

    题目

    在一个无序数组里有99个不重复的正整数,范围是1~100,唯独缺少1个1~100中的整数。如何找出这个缺失的整数?

    解决方案

    解决方法一

    创建一个哈希表,以1到100这100个整数为Key。然后遍历整个数组,每读到一个整数,就定位到哈希表中对应的Key,然后删除这个Key。由于数组中缺少1个整数,哈希表最终一定会有99个Key被删除,从而剩下1个唯一的Key。这个剩下的Key就是那个缺失的整数。假设数组长度是n,那么该解法的时间复杂度是O(n),空间复杂度是O(n)。

    解决方法二

    先把数组元素从小到大进行排序,然后遍历已经有序的数组,如果发现某两个相邻元素并不连续,说明缺少的就是这两个元素之间的整数。假设数组长度是n,如果用时间复杂度为O(nlogn)的排序算法进行排序,那么该解法的时间复杂度是O(nlogn),空间复杂度是O(1)。

    解决方法三

    先算出1+2+3+…+100的和,然后依次减去数组里的元素,最后得到的差值,就是那个缺失的整数。假设数组长度是n,那么该解法的时间复杂度是O(n),空间复杂度是O(1)。
    package arithmetic.com.ty.binary;
    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Random;
    
    public class LockData {
        public static void main(String args[])
        {
             //随机生成1-100的一个数作为缺失数
             Random rand = new Random();
             int miss=0;
             miss=rand.nextInt(100)+1;
             System.out.println("缺失的数为"+miss);
             //生成缺失了一个数之后的数组
             int[] missArray = new int[99];
             int j=0;
             for(int i=1;i<=100;i++)
             {
                 if(i!=miss)
                 {
                      missArray[j] = i;
                      j++;
                 }
             }
             System.out.println("解法一:"+solution1(missArray));
             System.out.println("解法二:"+solution2(missArray));
             System.out.println("解法三:"+solution3(missArray));
        }
        /*解法一
         * 创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。
         * 由于数组中缺少一个整数,最终一定有99个键对应的值等于1, 剩下一个键对应的值等于0。遍历修改后的HashMap,找到这个值为0的键。
         * 假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。**/
        public static int solution1(int[] missArray)
        {
             int missnumber = 0;
             //初始化map
             HashMap<Integer, Integer> myMap = new HashMap<Integer, Integer>(); 
             for(int i=1;i<=100;i++)
             {
                 myMap.put(i, 0);
             }
             //历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一
             for(int m:missArray)
             {
                 myMap.remove(m);
             }
             //遍历修改后的HashMap,找到这个值为0的键
             for (int key : myMap.keySet()) {
                 if(myMap.get(key)==0)
                 {
                      missnumber = key;
                      break;
                 }
              }
             return missnumber;
        }
        /*
         * 解法二:
         * 先把数组元素进行排序,然后遍历数组,检查任意两个相邻元素数值是否是连续的。如果不连续,则中间缺少的整数就是所要寻找的;如果全都连续,则缺少的整数不是1就是100。
         * 假设数组长度是N,如果用时间复杂度为O(N*LogN)的排序算法进行排序,那么该解法的时间复杂度是O(N*LogN),空间复杂度是O(1)。
         */
        public static int solution2(int[] missArray)
        {
             int missnumber = 0;
             //数组排序
             Arrays.sort(missArray);
             //检查是否相邻
             for(int i=0;i<missArray.length-1;i++)
             {
                 if(missArray[i+1]-missArray[i]!=1)
                 {
                      missnumber=missArray[i]+1;
                 }
             }
             //如果检查到相邻则返回否则缺失的数为100
             if(missnumber!=0)
             {
                 return missnumber;
             }
             else {
                 return 100;
             }
        }
        /*
         * 解法三:
         * 很简单也很高效的方法,先算出1+2+3....+100的合,然后依次减去数组里的元素,最后得到的差,就是唯一缺失的整数。
         * 假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。
         */
        public static int solution3(int[] missArray)
        {
             int missnumber=0;
             int all = 0;
             int missall = 0;
             for(int i=1;i<=100;i++)
             {
                 all = all+i;    
             }
             for(int m:missArray)
             {
                 missall = missall+m;
             }
             return all-missall;
        }
    }

    问题扩展

    题目第1次扩展:
    一个无序数组里有若干个正整数,范围是1~100,其中99个整数都出现了偶数次,只有1个整数出现了奇数次,如何找到这个出现奇数次的整数?

    首先得知道异或运算:

    相同位得0,不同位得1。如果一个数出现了偶数次,比如2次、4次。。。那么所有结果就变成0了,只有奇数次的整数会被留下。

    代码:

         /*
          * 遍历整个数组,依次做异或运算。由于异或在位运算时相同为0,不同为1,因此所有出现偶数次的整数都会相互抵消变成0,只有唯一出现奇数次的整数会被留下。
          * 假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。**/
         public static int solution2(int[] missArray)
         {
              int missnumber = 0;
              for(int m:missArray)
              {
                  missnumber = missnumber^m;
              }
              return missnumber;
         }
    题目第2次扩展:
    假设一个无序数组里有若干个正整数,范围是1~100,其中有98个整数出现了偶数次,只有2个整数出现了奇数次,如何找到这2个出现奇数次的整数?

    思路:

    把2个出现了奇数次的整数命名为A和B。遍历整个数组,然后依次做异或运算,进行异或运算的最终结果,等同于A和B进行异或运算的结果。在这个结果中,至少会有一个二进制位是1(如果都是0,说明A和B相等,和题目不相符)。举个例子,给出一个无序数组{4,1,2,2,5,1,4,3},所有元素进行异或运算的结果是00000110B。

    选定该结果中值为1的某一位数字,如00000110B的倒数第2位是1,这说明A和B对应的二进制的倒数第2位是不同的。其中必定有一个整数的倒数第2位是0,另一个整数的倒数第2位是1。根据这个结论,可以把原数组按照二进制的倒数第2位的不同,分成两部分,一部分的倒数第2位是0,另一部分的倒数第2位是1。由于A和B的倒数第2位不同,所以A被分配到其中一部分,B被分配到另一部分,绝不会出现A和B在同一部分,另一部分既没有A,也没有B的情况。

    代码:

        public static void solution2(int[] missArray) {
             int temp = 0;
             //数组所有元素异或。数组所有元素异或结果其实就是两个奇数次整数的异或结果,因为一个整数出现偶数次,异或一定是0.
             for(int m:missArray) {
                 temp = temp^m;
             }
             
             int site=1;
             /*
              * 找到1位为1的元素。
              * 例如无序数组{4,1,2,2,5,1,4,3},异或后的temp=00000110B。
              * 1<<site & temp == 0则代表第site位都是0,若果不等于0,说明找到1了。site就是1的位数
              */
             while((temp & (1 << site))==0) {
                 site++;
             }
             
             int num1 = 0;
             int num2 = 0;
             for(int m:missArray) {
                 //根据上面获得到的第site位为1的情况,取第site位为1的所有数,循环做异或,另外一组做异或。保证A、B是分开的
                 if(getBit(m, site)) {
                      num1 = num1^m;
                 }
                 
                 else {
                      num2 = num2^m;
                 }
             }
             System.out.println("奇数的数为"+num1);
             System.out.println("奇数的数为"+num2);
        }
        
        //获取 整数 num 的第 i 位的值
        private static boolean getBit(int num, int i) {
            //true 表示第i位为1,否则为0。a & b == 1 说明a b都是1
            return ((num & (1 << i)) != 0);
        }
  • 相关阅读:
    【Git&GitHub idea中使用Git 03】
    【Git&GitHub 本地库和远程库交互 02】
    【Git&GitHub 本地库操作 01】
    C Primer Plus学习笔记【11章节】
    Python包,模块理解,以及通过元类自动化注册属性。
    pandas.DataFrame.from_dict的使用介绍
    C Primer Plus学习笔记 第10章 编程练习
    CMOS与BIOS的区别(转帖)
    Python 元类详解 __new__、__init__、__call__[补充说明]
    Python 元类详解 __new__、__init__、__call__[收官之作]
  • 原文地址:https://www.cnblogs.com/alimayun/p/12793349.html
Copyright © 2011-2022 走看看