找出缺失整数

题目:一个无序数组里有99个不重复正整数,范围从1到100,唯独缺少一个整数。如何找出这个缺失的整数?

解法一: 

创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。

由于数组中缺少一个整数,最终一定有99个键对应的值等于1, 剩下一个键对应的值等于0。遍历修改后的HashMap,找到这个值为0的键。

假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。

解法二:

先把数组元素进行排序,然后遍历数组,检查任意两个相邻元素数值是否是连续的。如果不连续,则中间缺少的整数就是所要寻找的;如果全都连续,则缺少的整数不是1就是100。

假设数组长度是N,如果用时间复杂度为O(N*LogN)的排序算法进行排序,那么该解法的时间复杂度是O(N*LogN),空间复杂度是O(1)。

解法三:

很简单也很高效的方法,先算出1+2+3....+100的合,然后依次减去数组里的元素,最后得到的差,就是唯一缺失的整数。

假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。
  1 java代码如下
  2 
  3 import java.util.Arrays;
  4 import java.util.HashMap;
  5 import java.util.Random;
  6 //寻找缺失的整数
  7 //一个无序数组里有99个不重复正整数,范围从1到100,唯独缺少一个整数。如何找出这个缺失的整数
  8 public class LackData {
  9      public static void main(String args[])
 10      {
 11           //随机生成1-100的一个数作为缺失数
 12           Random rand =new Random();
 13           int miss=0;
 14           miss=rand.nextInt(100)+1;
 15           System.out.println("缺失的数为"+miss);
 16           //生成缺失了一个数之后的数组
 17           int[] missArray = new int[99];
 18           int j=0;
 19           for(int i=1;i<=100;i++)
 20           {
 21               if(i!=miss)
 22               {
 23                    missArray[j] = i;
 24                    j++;
 25               }
 26           }
 27           System.out.println("解法一:"+solution1(missArray));
 28           System.out.println("解法二:"+solution2(missArray));
 29           System.out.println("解法三:"+solution3(missArray));
 30      }
 31      /*解法一
 32       * 创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。
 33       * 由于数组中缺少一个整数,最终一定有99个键对应的值等于1, 剩下一个键对应的值等于0。遍历修改后的HashMap,找到这个值为0的键。
 34       * 假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。**/
 35      public static int solution1(int[] missArray)
 36      {
 37           int missnumber = 0;
 38           //初始化map
 39           HashMap<Integer, Integer> myMap = new HashMap<Integer, Integer>(); 
 40           for(int i=1;i<=100;i++)
 41           {
 42               myMap.put(i, 0);
 43           }
 44           //历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一
 45           for(int m:missArray)
 46           {
 47               myMap.put(m, myMap.get(m)+1);
 48           }
 49           //遍历修改后的HashMap,找到这个值为0的键
 50           for (int key : myMap.keySet()) {
 51               if(myMap.get(key)==0)
 52               {
 53                    missnumber = key;
 54                    break;
 55               }
 56            }
 57           return missnumber;
 58      }
 59      /*
 60       * 解法二:
 61       * 先把数组元素进行排序,然后遍历数组,检查任意两个相邻元素数值是否是连续的。如果不连续,则中间缺少的整数就是所要寻找的;如果全都连续,则缺少的整数不是1就是100。
 62       * 假设数组长度是N,如果用时间复杂度为O(N*LogN)的排序算法进行排序,那么该解法的时间复杂度是O(N*LogN),空间复杂度是O(1)。
 63       */
 64      public static int solution2(int[] missArray)
 65      {
 66           int missnumber = 0;
 67           //数组排序
 68           Arrays.sort(missArray);
 69           //检查是否相邻
 70           for(int i=0;i<missArray.length-1;i++)
 71           {
 72               if(missArray[i+1]-missArray[i]!=1)
 73               {
 74                    missnumber=missArray[i]+1;
 75               }
 76           }
 77           //如果检查到相邻则返回否则缺失的数为100
 78           if(missnumber!=0)
 79           {
 80               return missnumber;
 81           }
 82           else {
 83               return 100;
 84           }
 85      }
 86      /*
 87       * 解法三:
 88       * 很简单也很高效的方法,先算出1+2+3....+100的合,然后依次减去数组里的元素,最后得到的差,就是唯一缺失的整数。
 89       * 假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。
 90       */
 91      public static int solution3(int[] missArray)
 92      {
 93           int missnumber=0;
 94           int all = 0;
 95           int missall = 0;
 96           for(int i=1;i<=100;i++)
 97           {
 98               all = all+i;
 99           }
100           for(int m:missArray)
101           {
102               missall = missall+m;
103           }
104           return all-missall;
105      }
106 }
题目扩展:一个无序数组里有若干个正整数,范围从1到100,其中99个整数都出现了偶数次,只有一个整数出现了奇数次(比如1,1,2,2,3,3,4,5,5),如何找到这个出现奇数次的整数?
解法一:
创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。由于数组中缺少一个整数,最终一定有99个键对应的值为偶数, 剩下一个键对应的值为奇数。遍历修改后的HashMap,找到这个值为奇数 的键。假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。
解法二 遍历整个数组,依次做异或运算。由于异或在位运算时相同为0,不同为1,因此所有出现偶数次的整数都会相互抵消变成0,只有唯一出现奇数次的整数会被留下。假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。
  1 import java.util.HashMap;
  2 import java.util.Random;
  3 import javax.xml.transform.Templates;
  4 //题目扩展:一个无序数组里有若干个正整数,范围从1到100,其中99个整数都出现了偶数次,只有一个整数出现了奇数次(比如1,1,2,2,3,3,4,5,5),如何找到这个出现奇数次的整数?
  5 public class LackDataExtension {
  6      public static void main(String args[])
  7      {
  8           //随机生成1-100的一个数作为出现基数次的数
  9           Random rand =new Random();
 10           int miss=0;
 11           miss=rand.nextInt(100)+1;
 12           System.out.println("缺失的数为"+miss);
 13           //存储每个数出现的次数 key为数value为该数出现的次数
 14            HashMap<Integer, Integer> store = new HashMap<Integer,Integer>();
 15            int number = 0;
 16            for(int i=1;i<=100;i++)
 17            {
 18                int temp = 0;
 19                if(i!=miss)
 20                {
 21                     temp = randEven();
 22                     store.put(i, temp);
 23                     number = number+temp;
 24                }
 25                else
 26                {
 27                     temp = randOdd();
 28                     store.put(i, temp);
 29                     number = number+temp;
 30               }
 31            }
 32           //生成缺99个整数都出现了偶数次,只有一个整数出现了奇数次的数组
 33           int[] missArray = new int[number];
 34           int temp=0;
 35           for(int i=1;i<=100;i++)
 36           {    
 37               for(int j=0;j<store.get(i);j++)
 38               {
 39                    missArray[temp] = i;
 40                    temp++;
 41               }
 42           }
 43           System.out.println("解法一:"+solution1(missArray));
 44           System.out.println("解法二:"+solution2(missArray));
 45      }
 46      /*
 47       * 解法1
 48       * 创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。
 49       * 由于数组中缺少一个整数,最终一定有99个键对应的值为偶数, 剩下一个键对应的值为奇数。遍历修改后的HashMap,找到这个值为奇数的键。
 50       * 假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。**/
 51      public static int solution1(int[] missArray)
 52      {
 53           int missnumber = 0;
 54           //初始化map
 55           HashMap<Integer, Integer> myMap = new HashMap<Integer, Integer>(); 
 56           for(int i=1;i<=100;i++)
 57           {
 58               myMap.put(i, 0);
 59           }
 60           //历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一
 61           for(int m:missArray)
 62           {
 63               myMap.put(m, myMap.get(m)+1);
 64           }
 65           //遍历修改后的HashMap,找到这个值为0的键
 66           for (int key : myMap.keySet()) {
 67               if(myMap.get(key)%2==1)
 68               {
 69                    missnumber = key;
 70                    break;
 71               }
 72            }
 73           return missnumber;
 74      }
 75      /*
 76       * 解法2
 77       * 遍历整个数组,依次做异或运算。由于异或在位运算时相同为0,不同为1,因此所有出现偶数次的整数都会相互抵消变成0,只有唯一出现奇数次的整数会被留下。
 78       * 假设数组长度是N,那么该解法的时间复杂度是O(N),空间复杂度是O(1)。**/
 79      public static int solution2(int[] missArray)
 80      {
 81           int missnumber = 0;
 82           for(int m:missArray)
 83           {
 84               missnumber = missnumber^m;
 85           }
 86           return missnumber;
 87      }
 88      //生成1-10的奇数
 89      public static int randOdd()
 90      {
 91           Random rand =new Random();
 92           int temp = rand.nextInt(5)+1;
 93           return temp*2-1;
 94      }
 95      //生成1-10的偶数
 96      public static int randEven()
 97      {
 98           Random rand =new Random();
 99           int temp = rand.nextInt(5)+1;
100           return temp*2;
101      }
102 }
题目第二次扩展:一个无序数组里有若干个正整数,范围从1到100,其中98个整数都出现了偶数次,只有两个整数出现了奇数次(比如1,1,2,2,3,4,5,5),如何找到这个出现奇数次的整数?
 
解法:

遍历整个数组,依次做异或运算。由于数组存在两个出现奇数次的整数,所以最终异或的结果,等同于这两个整数的异或结果。这个结果中,至少会有一个二进制位是1(如果都是0,说明两个数相等,和题目不符)。

举个例子,如果最终异或的结果是5,转换成二进制是00000101。此时我们可以选择任意一个是1的二进制位来分析,比如末位。把两个奇数次出现的整数命名为A和B,如果末位是1,说明A和B转为二进制的末位不同,必定其中一个整数的末位是1,另一个整数的末位是0。

根据这个结论,我们可以把原数组按照二进制的末位不同,分成两部分,一部分的末位是1,一部分的末位是0。由于A和B的末位不同,所以A在其中一部分,B在其中一部分,绝不会出现A和B在同一部分,另一部分没有的情况。

这样一来就简单了,我们的问题又回归到了上一题的情况,按照原先的异或解法,从每一部分中找出唯一的奇数次整数即可。

假设数组长度是N,那么该解法的时间复杂度是O(N)。把数组分成两部分,并不需要借助额外存储空间,完全可以在按二进制位分组的同时来做异或运算,所以空间复杂度仍然是O(1)。
  1 import java.util.HashMap;
  2 import java.util.Random;
  3 import javax.xml.transform.Templates;
  4 //题目扩展:一个无序数组里有若干个正整数,范围从1到100,其中98个整数都出现了偶数次,只有两个整数出现了奇数次(比如1,1,2,2,3,4,5,5),如何找到这个出现奇数次的整数?
  5 public class LackDataExtensionTwo {
  6      public static void main(String args[])
  7      {
  8           //随机生成1-100的两个数作为出现奇数次的数
  9           Random rand =new Random();
 10           int miss=0;
 11           miss=rand.nextInt(100)+1;
 12           System.out.println("奇数次的数1为"+miss);
 13           int miss1=0;
 14           do
 15           {
 16               miss1=rand.nextInt(100)+1;
 17           }
 18           while(miss1!=0&&miss1==miss);
 19           System.out.println("奇数次的数2为"+miss1);
 20           //存储每个数出现的次数 key为数value为该数出现的次数
 21            HashMap<Integer, Integer> store = new HashMap<Integer,Integer>();
 22            int number = 0;
 23            for(int i=1;i<=100;i++)
 24            {
 25                int temp = 0;
 26                if(i!=miss&&i!=miss1)
 27                {
 28                     temp = randEven();
 29                     store.put(i, temp);
 30                     number = number+temp;
 31                }
 32                else
 33                {
 34                     temp = randOdd();
 35                     store.put(i, temp);
 36                     number = number+temp;
 37               }
 38            }
 39           //生成缺98个整数都出现了偶数次,只有二个整数出现了奇数次的数组
 40           int[] missArray = new int[number];
 41           int temp=0;
 42           for(int i=1;i<=100;i++)
 43           {    
 44               for(int j=0;j<store.get(i);j++)
 45               {
 46                    missArray[temp] = i;
 47                    temp++;
 48               }
 49           }
 50           System.out.print("解法一:");
 51           solution1(missArray);
 52           System.out.print("解法二:");
 53           solution2(missArray);
 54      }
 55      /*
 56       * 解法1
 57       * 创建一个HashMap,以1到100为键,值都是0 。然后遍历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一。
 58       * 由于数组中缺少一个整数,最终一定有98个键对应的值为偶数, 剩下二个键对应的值为奇数。遍历修改后的HashMap,找到这个值为奇数的键。
 59       * 假设数组长度是N,那么该解法的时间复杂度是O(1),空间复杂度是O(N)。**/
 60      public static void solution1(int[] missArray)
 61      {
 62           int missnumber = 0;
 63           //初始化map
 64           HashMap<Integer, Integer> myMap = new HashMap<Integer, Integer>(); 
 65           for(int i=1;i<=100;i++)
 66           {
 67               myMap.put(i, 0);
 68           }
 69           //历整个数组,每读到一个整数,就找到HashMap当中对应的键,让其值加一
 70           for(int m:missArray)
 71           {
 72               myMap.put(m, myMap.get(m)+1);
 73           }
 74           //遍历修改后的HashMap,找到这个值为0的键
 75           for (int key : myMap.keySet()) {
 76               if(myMap.get(key)%2==1)
 77               {
 78                    missnumber = key;
 79                    System.out.println("奇数的数为"+missnumber);
 80               }
 81            }
 82      }
 83      /*
 84       * 解法2
 85       * 遍历整个数组,依次做异或运算。由于数组存在两个出现奇数次的整数,所以最终异或的结果,等同于这两个整数的异或结果。这个结果中,至少会有一个二进制位是1(如果都是0,说明两个数相等,和题目不符)。
 86       * 举个例子,如果最终异或的结果是5,转换成二进制是00000101。此时我们可以选择任意一个是1的二进制位来分析,比如末位。
 87       * 把两个奇数次出现的整数命名为A和B,如果末位是1,说明A和B转为二进制的末位不同,必定其中一个整数的末位是1,另一个整数的末位是0。
 88       * 根据这个结论,我们可以把原数组按照二进制的末位不同,分成两部分,一部分的末位是1,一部分的末位是0。
 89       * 由于A和B的末位不同,所以A在其中一部分,B在其中一部分,绝不会出现A和B在同一部分,另一部分没有的情况。
 90       * 这样一来就简单了,按照原先的异或解法,从每一部分中找出唯一的奇数次整数即可。
 91       * 假设数组长度是N,那么该解法的时间复杂度是O(N)。把数组分成两部分,并不需要借助额外存储空间,完全可以在按二进制位分组的同时来做异或运算,所以空间复杂度仍然是O(1)。
 92 **/
 93      public static void solution2(int[] missArray)
 94      {
 95           int temp = 0;
 96           //数组所有元素异或
 97           for(int m:missArray)
 98           {
 99               temp = temp^m;
100           }
101           int site=1;
102           //找到1位为1的元素
103           while((temp & (1 << site))==0)
104           {
105               site++;
106           }
107           int num1 = 0;
108           int num2 = 0;
109           for(int m:missArray)
110           {
111               if(getBit(m, site))
112               {
113                    num1 = num1^m;
114               }
115               else {
116                    num2 = num2^m;
117               }
118           }
119           System.out.println("奇数的数为"+num1);
120           System.out.println("奇数的数为"+num2);
121      }
122      //生成1-10的奇数
123      public static int randOdd()
124      {
125           Random rand =new Random();
126           int temp = rand.nextInt(5)+1;
127           return temp*2-1;
128      }
129      //生成1-10的偶数
130      public static int randEven()
131      {
132           Random rand =new Random();
133           int temp = rand.nextInt(5)+1;
134           return temp*2;
135      }
136      //获取 整数 num 的第 i 位的值
137     private static boolean getBit(int num, int i)
138     {
139         return ((num & (1 << i)) != 0);//true 表示第i位为1,否则为0
140     }
141 }

 

posted @ 2017-12-18 15:11  icychen  阅读(741)  评论(0编辑  收藏  举报