面试题:寻找缺失的整数
题目
在一个无序数组里有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); }
posted on 2020-04-28 14:03 阿里-马云的学习笔记 阅读(913) 评论(0) 编辑 收藏 举报