一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
代码
解法一
比较笨,先排序,是的相同的数字挨在一起,这样不同的数字必然和前后数字都不一致,对于数组的第一个和最后一个元素,则需要单独处理。
public static List<Integer> findSignleAppearNumber(int[] array) { if (array == null || array.length <= 1) { return null; } // 如果数组的长度是奇数,不可能出现两个只出现一次的数字 if (array.length % 2 == 1) { return null; } List<Integer> result = new ArrayList<>(2); if (array.length == 2) { for (int i : array) { result.add(i); } return result; } // 对数组排序,使得出现两次的数字可以挨着 Arrays.sort(array); // 根据前面的判断条件,此时数组长度大于等于4 // 如果第一个数字和第二个数字不相等,说明第一个数字就是只出现一次的数字 if (array[0] != array[1]) { result.add(array[0]); } // 为了和前后数字对比,第一个数字和最后一个数字不比较 for (int i = 1; i < array.length - 1; i++) { // 如果一个数字和其前后两个数字都不相等,说明该数字只出现一次 if (array[i] != array[i - 1] && array[i] != array[i + 1]) { result.add(array[i]); } } // 如果遍历没有找到,说明数组的最后一个数字是要找的 if (result.size() < 2) { result.add(array[array.length - 1]); } return result; } public static void main(String[] args) { int[] array = {1, 2, 2, 3}; List<Integer> list = findSignleAppearNumber(array); for (Integer i : list) { System.out.println(i); } }
解法二
借助异或,任何一个数字异或它自己都等于0,只有都为0或者都为1,才为1,如果不相同则为0
public static List<Integer> findSignleAppearNumber2(int[] array) { if (array == null || array.length <= 1) { return null; } // 如果数组的长度是奇数,不可能出现两个只出现一次的数字 if (array.length % 2 == 1) { return null; } List<Integer> result = new ArrayList<>(2); if (array.length == 2) { for (int i : array) { result.add(i); } return result; } // 任何一个数字异或它自己都等于0,所以xor的结果是两个只出现一次的数字的异或结果 int xor = 0; for (int i = 0; i < array.length; i++) { xor ^= array[i]; } // 找出这个异或结果二进制第一次位为1的位置 // 以此来将原数组切割为两个子数组,每个子数组包含一个只出现一次的数字 // 因为位为1,说明两个数字在该位上不相同,即必然一个在该位上是0,另一个是1,这是异或的规则所致 // 所以以此来划分数组,两个数字必然在不同子数组之中 int bitIndex = findFirstBitIs1(xor); int n1 = 0; int n2 = 0; for (int i = 0; i < array.length; i++) { if (is1OfBitIndex(array[i], bitIndex)) { n1 ^= array[i]; } else { n2 ^= array[i]; } } result.add(n1); result.add(n2); return result; }