问题一:在一个整数数组中,除了一个数之外,其他的数出现的次数都是两次,求出现一次的数,要求时间复杂度尽可能的小。例如数组{1,2,2,3,3,6,6},出现一次的数是1.
从题目的描述可以看出,数组中只有一个数字出现了一次,其他的数字都出现两次,联想到异或运算的特点:任何一个数字和自己做异或运算的结果都是0,任何数字和0运算的结果都是本身。根据上述特点,可以考虑从数组的第一个元素开始,逐个和后面的元素做异或操作,最后的计算结果就是要找的只出现一次的数。
算法实现如下:
public class main { public static void main(string[] args){ int[] nums = new int[]{1,2,2,3,3,4,4,1,23}; system.out.println(getnumappearsonce(nums)); } public static int getnumappearsonce(int[] nums){ if(nums == null || nums.length <= 2){ throw new illegalargumentexception("nums size must bigger than 2"); } int result = 0; for(int i=0;i){ result ^= nums[i]; } return result; } }
由于只需要遍历一遍数组就可以找到结果,因此上述算法的时间复杂度为o(n),其中n为数组的长度
发散思维:假如要求的数组里面有两个数出现的次数是1,其他出现的次数都是2,如何找出这两个数呢?
根据上面问题一的思路,我们依然从头到位对数组做异或运算,得到的最终结果应该是两个不同数字做异或运算后的值。因为两个数字不相同,最终的结果也肯定不是0,切结果对应的二进制位中至少有一个为1,我们找到二进制位中第一个是1的位置,即为n,然后根据地n为是1还是0把数组分为两个子数组,第一个子数组中的每个数的二进制位的第n位都是1,另外一个是0,这样分完后,相同的数字肯定会被分到同一个子数组中,并且每个子数组中只包含一个只出现一次的数,根据问题一,我们可以很方便的求出两个只出现一次的数,实现如下:
public class main { public static void main(string[] args){ int[] nums = new int[]{1,2,2,3,3,4,4,1,23,43}; system.out.println(getnumsappearsonce(nums)); } public static int getnumappearsonce(int[] nums){ if(nums == null || nums.length <= 2){ throw new illegalargumentexception("nums size must bigger than 2"); } int result = 0; for(int i=0;i){ result ^= nums[i]; } return result; } public static list getnumsappearsonce(int[] nums){ if(nums == null || nums.length <= 2){ throw new illegalargumentexception("nums size must bigger than 2"); } list result = new arraylist (2); int temp = getnumappearsonce(nums); int index = findfirstbitidone(temp); int num1 = 0,num2 = 0; for(int i=0;i ){ if(isbitone(nums[i],index)){ num1 ^= nums[i]; }else{ num2 ^= nums[i]; } } result.add(num1); result.add(num2); return result; } private static boolean isbitone(int num, int index) { num = num >> index; return (num & 1) == 1; } private static int findfirstbitidone(int temp) { int index = 0; while((temp & 1) == 0){ temp = temp >> 1; index ; } return index; } }