转自:http://blog.youkuaiyun.com/yangcs2009/article/details/38384657
问题1:1-N的自然数中,少了一个,找出这个数
(1)求和-容易溢出
Sum=1+2+...+N=(N+1)N/2,然后遍历数列每次从S1中减去当前的数字,最后剩下的数字就是所求。
为了防止溢出我们可以每次在S1大于一定的数字后,就去减,然后继续求和,再大于就继续减,以此类推。
- public int find_Miss_Number_By_Sum(int array[], int n) {
- int sum = 0;
- if (array.length != n - 1)
- throw new IllegalArgumentException("数组内的自然数目少于n-1");
- for (int i : array) {
- sum += i;
- }
- return n * (n + 1) / 2 - sum;
- }
(2)异或^
Y=1^2^3...^N,然后遍历数列每次异或当前的数字,最后剩下的就是要求的数字
任何数异或自己都等于0,任何数异或0都等于他自己
- public int find_Miss_Number_By_XOR(int array[], int n) {
- int result = 0;
- if (array.length != n - 1)
- throw new IllegalArgumentException("数组内的自然数目少于n-1");
- for (int i = 0; i < array.length; i++) {
- result = result ^ array[i];
- }
- for (int i = 0; i < n; i++) {
- result=result^(i+1);
- }
- return result;
- }
(3)O(N)时间的移动-排序
将a[i]移动到b[a[i]],使得数组有序,然后找出空着的位置
- public int find_Miss_Number_By_Sort(int array[], int n) {
- int result = 0;
- if (array.length != n - 1)
- throw new IllegalArgumentException("数组内的自然数目少于n-1");
- int b[] = new int[n];
- for (int i = 0; i < array.length; i++) {
- b[array[i] - 1] = array[i];
- }
- for (int i = 0; i < b.length; i++) {
- if (b[i] == 0) {
- result = i + 1;
- break;
- }
- }
- return result;
- }
(4)O(NlogN)时间的移动-排序
用快排的思想,在1-N中选取游标X对数组快排一次,如果X被放在a[X-1]的位置上那么,要找的数字在X-N之间
否则X被放在a[X-2]的位置上 要找的数字在1-X-1之间 递归求解,直到找的要找的数字。
问题2:1-N个自然数,少了两个,找出这两个数
(1)求和-容易溢出
S1=1+2+...+N=(N+1)N/2
S2=12+22+...+N2=(N+1)(2N+1)N/6
- import junit.framework.TestCase;
- public class Find_Miss_Numbers_By_Sum_And_Mul extends TestCase {
- //1-N的自然数中,少了一个,找出这个数
- //方法:求和,求积 x1+x2 和 x1*x2都知道了以后,求出x1和x2
- public void find_Miss_Number_By_Sum_and_Product(Integer array[]) {
- int sum = 0; //保存缺少的两个数的和
- int product = 1;//保存缺少的两个数的积
- int n = array.length + 2;
- // 1-n的自然数的和减去数组中所有元素的和,即为缺少的两个数之和
- for (Integer i : array) {
- sum += i;
- }
- sum = n * (n + 1) / 2 - sum;
- // 1-n的自然数的积除以数组中所有元素,即为缺少的两个数之积
- for (int j = 1; j < n + 1; j++) {
- product = product * j;
- }
- for (Integer integer : array) {
- product /= integer;
- }
- System.out.println("数组元素的和为" + sum);
- System.out.println("数组元素的积为" + product);
- System.out.println("缺少的第一个数为" + getSolution_left(sum, product));
- System.out.println("缺少的第二个数为" + getSolution_right(sum, product));
- }
- // 根据x1+x2 和 x1*x2都x1和x2
- public int getSolution_left(int sum, int product) {
- return (int)(sum - Math.sqrt(sum * sum - 4 * product)) / 2;
- }
- public int getSolution_right(int sum, int product) {
- return (int) (sum + Math.sqrt(sum * sum - 4 * product)) / 2;
- }
- public void test() {
- Integer a[] = { 1, 2, 3, 5, 7, 8, 9 };
- find_Miss_Number_By_Sum_and_Product(a);
- }
- }
对于少了K个数的情况,如果K很少,我们可以找出K个和上面类似的函数,计算总体值,然后用解K元一次方程得到结果,但要注意函数的选择
(2)异或
按照上面同样的方法,求出最后的值P等于两个数的异或
确定P从低位到高位的第一个1是第i位,现在用快排的思想,将数列分成两个区间A和B,其中A中第i位是0,B中的第i位是1,然后1-N中第i位是0的异或A,最后的结果就为缺少的第一个数。同理在B中求出第二个数(也可在求出第一个数后,用该数异或P,结果即为缺少的第二个数)。
- import java.util.ArrayList;
- import java.util.List;
- import junit.framework.TestCase;
- public class Find_Miss_Numbers_By_Xor extends TestCase {
- /**
- * @param array 包含所有整数的数组
- * @param subArray 缺少整数的数组
- * @return 缺少的整数的异或值
- */
- public static int find_Miss_Number_By_XOR(Integer array[],Integer subArray[]) {
- int result = 0;
- for (Integer e : array) {
- result ^= e;
- }
- for (Integer e : subArray) {
- result ^= e;
- }
- return result;
- }
- // 获取最低位1的位置
- public static int get_mid_By_xor_result(int number) {
- int location = 0;
- while (number % 2 == 0) {
- location++;
- number /= 2;
- }
- return location;
- }
- //返回一个数组中第i位为1的所有的数构成的数组
- private static Integer[] divid_Array(Integer array[], int i) {
- List<Integer> list = new ArrayList<Integer>();
- for (Integer e : array) {
- int temp = e;
- for (int j = 0; j < i; j++) {
- temp = e / 2;
- }
- if (temp % 2 == 1) {
- list.add(e);
- }
- }
- Integer[] result = new Integer[list.size()];
- for (int j = 0; j < list.size(); j++) {
- result[j] = list.get(j);
- }
- return result;
- }
- /**
- * @param array 包含所有整数的数组
- * @param subArray 缺少整数的数组
- */
- public static void getMissNumber(Integer array[], Integer subArray[]) {
- int xor = find_Miss_Number_By_XOR(array, subArray);
- System.out.println("异或的结果为" + xor);
- int mid = get_mid_By_xor_result(xor);
- System.out.println("最低位1的位置" + mid);
- // 数组A的元素为:
- System.out.println("数组A的元素为:");
- Integer[] array1 = divid_Array(array, mid);
- for (Integer e : array1) {
- System.out.print(e + "、");
- }
- // 数组B的元素为:
- System.out.println();
- System.out.println("数组B的元素为:");
- Integer[] array2 = divid_Array(subArray, mid);
- for (Integer e : array2) {
- System.out.print(e + "、");
- }
- System.out.println();
- System.out.println("缺少的第一个数为:");
- int solution1 = find_Miss_Number_By_XOR(array1, array2);
- System.out.println(solution1);
- System.out.println("缺少的第二个数为:");
- int solution2 = solution1 ^ xor;
- System.out.println(solution2);
- }
- public static void main(String[] args) {
- Integer array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12};
- Integer subArray[] = { 1, 2, 4, 5, 6, 7, 8, 9,11,12 };
- getMissNumber(array, subArray);
- }
- }
(3)O(N)时间移动-排序
跟上面一样,实际上这种方法对于少了K个数的情况都能适用。
- import junit.framework.TestCase;
- public class Find_Miss_Numbers_By_Move extends TestCase {
- // 1-N的自然数中,少了一个,找出这个数
- // 方法:将a[i]移动到a[a[i]],使得数组有序,然后找出空着的位置
- public void find_Miss_Number_By_Move(int array[], int n) {
- int b[] = new int[n];
- for (int i : array) {
- b[i - 1] = i;
- }
- System.out.print("数组的元素为:");
- for (int j : b) {
- if (j != 0) {
- System.out.print(j + "、");
- }
- }
- System.out.println();
- System.out.print("缺少的数为:");
- for (int k = 0; k < b.length; k++) {
- if (b[k] == 0) {
- System.out.print(k + 1 + "、");
- }
- }
- }
- public void test() {
- int a[] = { 1, 2, 4, 7, 8, 9 };
- find_Miss_Number_By_Move(a, 9);
- }
- }
(4)O(NlogN)时间移动-排序
跟上面的方法一样
如果X被放在a[X-1]位置上,要找的两个数字在X-N之间
如果X被放在a[X-2]位置上,要找的数字一个在1-X-1间,一个在X-N之间
如果X被放在a[X-3]位置上,要找的数字都在1-X-1间
对于少了K个数字的情况,这种方法也可以做,但实现起来就比较复杂了
问题3:给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。
(1)异或
一个数跟自己偶数次异或是0,奇数次异或是自己
问题4:给你n个数,其中有且仅有两个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那两个数。
从头到尾异或一遍,你就得到了需要求的两个数异或后的值。这两个数显然不相等,异或出来的结果不为0。我们可以据此找出两个数的二进制表达中不同的一位,然后把所有这n个数分成两类,在那一位上是0的分成一类,在那一位上是1的分到另一类。对每一类分别使用前一个问题的算法。
代码和异或找两个数一样,将测试数据该成即可
- public static void main(String[] args) {
- Integer array[] = { 1,1,2, 2, 3,3,4, 4, 5,5,};
- Integer subArray[] = { 1, 2,2, 3,4, 4, 5,5,};
- getMissNumber(array, subArray);
- }
转http://blog.youkuaiyun.com/ncepuzhuang/article/details/8719341