zoukankan      html  css  js  c++  java
  • 1-N的自然数中,少了一个,找出这个数

    原文地址

    问题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);
    	}
  • 相关阅读:
    JS的数据类型(包含:7种数据类型的介绍、数据类型的转换、数据类型的判断)
    使用终端改变MAC默认截图存放地址
    Homebrew的安装及使用
    CSS多列布局(实例)
    CSS中元素各种居中方法(思维导图)
    用CSS伪类制作一个不断旋转的八卦图?
    DOSBOX的简单使用
    5分钟读懂JavaScript预编译流程
    基于栈虚拟机和基于寄存器虚拟机的比较
    js执行可视化
  • 原文地址:https://www.cnblogs.com/xqzt/p/5637108.html
Copyright © 2011-2022 走看看