• 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);
    	}
  • 相关阅读:
    软件配置管理的作用?软件配置包括什么?
    火火恍恍惚惚
    什么是软件测试?软件测试的目的与原则
    软件生存周期及其模型是什么?
    试述软件的概念和特点?软件复用的含义?构件包括哪些?
    一台客户端有三百个客户与三百个客户端有三百个客户对服务器施压,有什么区别?
    numpy的broadcast是怎么做的
    python到底是解释型语言还是需要编译的?
    python:删除类实例,仅仅只有动态属性会被删除,类属性不会被删除
    jupyter的kernel莫名其妙找不到,莫名其妙就中断
  • 原文地址:https://www.cnblogs.com/xiaoqiangzhaitai/p/5637402.html
Copyright © 2020-2023  润新知