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);
	}

 

posted on 2013-03-25 22:40  小-强-斋-太  阅读(243)  评论(0编辑  收藏  举报

导航