1-N的自然数中,少了一个,找出这个数

本文探讨了在一组连续自然数中寻找缺失数值的不同算法,包括利用求和、异或运算、快速排序思想及直接排序等多种方法,并进一步讨论了如何在出现多个缺失值或异常值(如奇数次出现的数值)的情况下高效解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题1:1-N的自然数中,少了一个,找出这个数

(1)求和-容易溢出

Sum=1+2+...+N=(N+1)N/2,然后遍历数列每次从S1中减去当前的数字,最后剩下的数字就是所求。

为了防止溢出我们可以每次在S1大于一定的数字后,就去减,然后继续求和,再大于就继续减,以此类推。

  1. public int find_Miss_Number_By_Sum(int array[], int n) {  
  2.    int sum = 0;  
  3.    if (array.length != n - 1)  
  4.        throw new IllegalArgumentException("数组内的自然数目少于n-1");  
  5.    for (int i : array) {  
  6.        sum += i;  
  7.    }  
  8.    return n * (n + 1) / 2 - sum;  
  9. }  

(2)异或^

Y=1^2^3...^N,然后遍历数列每次异或当前的数字,最后剩下的就是要求的数字

任何数异或自己都等于0,任何数异或0都等于他自己

  1. public int find_Miss_Number_By_XOR(int array[], int n) {  
  2.    int result = 0;  
  3.    if (array.length != n - 1)  
  4.        throw new IllegalArgumentException("数组内的自然数目少于n-1");  
  5.      
  6.    for (int i = 0; i < array.length; i++) {  
  7.        result = result  ^ array[i];  
  8.    }  
  9.    for (int i = 0; i < n; i++) {  
  10.        result=result^(i+1);  
  11.    }  
  12.    return result;  
  13.  
  14. }  

(3)O(N)时间的移动-排序

将a[i]移动到b[a[i]],使得数组有序,然后找出空着的位置

  1. public int find_Miss_Number_By_Sort(int array[], int n) {  
  2.    int result = 0;  
  3.  
  4.    if (array.length != n - 1)  
  5.        throw new IllegalArgumentException("数组内的自然数目少于n-1");  
  6.  
  7.    int b[] = new int[n];  
  8.  
  9.    for (int i = 0; i < array.length; i++) {  
  10.        b[array[i] - 1] = array[i];  
  11.    }  
  12.    for (int i = 0; i < b.length; i++) {  
  13.        if (b[i] == 0) {  
  14.            result = i + 1;  
  15.            break;  
  16.        }  
  17.    }  
  18.    return result;  
  19. }  

(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

  1. import junit.framework.TestCase;  
  2.  
  3. public class Find_Miss_Numbers_By_Sum_And_Mul extends TestCase {  
  4.      
  5.    //1-N的自然数中,少了一个,找出这个数  
  6.    //方法:求和,求积  x1+x2 和 x1*x2都知道了以后,求出x1和x2  
  7.    public void find_Miss_Number_By_Sum_and_Product(Integer array[]) {  
  8.  
  9.        int sum = 0; //保存缺少的两个数的和  
  10.        int product = 1;//保存缺少的两个数的积  
  11.        int n = array.length + 2;  
  12.  
  13.        // 1-n的自然数的和减去数组中所有元素的和,即为缺少的两个数之和  
  14.        for (Integer i : array) {  
  15.            sum += i;  
  16.        }  
  17.        sum = n * (n + 1) / 2 - sum;  
  18.  
  19.        // 1-n的自然数的积除以数组中所有元素,即为缺少的两个数之积  
  20.        for (int j = 1; j < n + 1; j++) {  
  21.            product = product * j;  
  22.        }  
  23.        for (Integer integer : array) {  
  24.            product /= integer;  
  25.        }  
  26.        System.out.println("数组元素的和为" + sum);  
  27.        System.out.println("数组元素的积为" + product);  
  28.        System.out.println("缺少的第一个数为" + getSolution_left(sum, product));  
  29.        System.out.println("缺少的第二个数为" + getSolution_right(sum, product));  
  30.  
  31.    }  
  32.    // 根据x1+x2 和 x1*x2都x1和x2  
  33.    public int getSolution_left(int sum, int product) {  
  34.        return (int)(sum - Math.sqrt(sum * sum - 4 * product)) / 2;  
  35.  
  36.    }  
  37.    public int getSolution_right(int sum, int product) {  
  38.        return (int) (sum + Math.sqrt(sum * sum - 4 * product)) / 2;  
  39.    }  
  40.  
  41.    public void test() {  
  42.        Integer a[] = { 1, 2, 3, 5, 7, 8, 9 };  
  43.        find_Miss_Number_By_Sum_and_Product(a);  
  44.    }  
  45.  
  46. }  

对于少了K个数的情况,如果K很少,我们可以找出K个和上面类似的函数,计算总体值,然后用解K元一次方程得到结果,但要注意函数的选择

(2)异或

按照上面同样的方法,求出最后的值P等于两个数的异或

确定P从低位到高位的第一个1是第i位,现在用快排的思想,将数列分成两个区间A和B,其中A中第i位是0,B中的第i位是1,然后1-N中第i位是0的异或A,最后的结果就为缺少的第一个数。同理在B中求出第二个数(也可在求出第一个数后,用该数异或P,结果即为缺少的第二个数)。

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.  
  4. import junit.framework.TestCase;  
  5.  
  6. public class Find_Miss_Numbers_By_Xor extends TestCase {  
  7.  
  8.    /**
  9.     * @param array 包含所有整数的数组
  10.     * @param subArray 缺少整数的数组
  11.     * @return 缺少的整数的异或值
  12.     */  
  13.    public static int find_Miss_Number_By_XOR(Integer array[],Integer subArray[]) {  
  14.        int result = 0;  
  15.  
  16.        for (Integer e : array) {  
  17.            result ^= e;  
  18.        }  
  19.        for (Integer e : subArray) {  
  20.            result ^= e;  
  21.        }  
  22.        return result;  
  23.    }  
  24.  
  25.    // 获取最低位1的位置  
  26.    public static int get_mid_By_xor_result(int number) {  
  27.        int location = 0;  
  28.        while (number % 2 == 0) {  
  29.            location++;  
  30.            number /= 2;  
  31.        }  
  32.        return location;  
  33.    }  
  34.   //返回一个数组中第i位为1的所有的数构成的数组  
  35.    private static Integer[] divid_Array(Integer array[], int i) {  
  36.  
  37.        List<Integer> list = new ArrayList<Integer>();  
  38.  
  39.        for (Integer e : array) {  
  40.            int temp = e;  
  41.              
  42.            for (int j = 0; j < i; j++) {  
  43.                temp = e / 2;  
  44.            }  
  45.            if (temp % 2 == 1) {  
  46.                list.add(e);  
  47.            }  
  48.  
  49.        }  
  50.        Integer[] result = new Integer[list.size()];  
  51.        for (int j = 0; j < list.size(); j++) {  
  52.            result[j] = list.get(j);  
  53.        }  
  54.        return result;  
  55.    }  
  56.  
  57.    /**
  58.     * @param array 包含所有整数的数组
  59.     * @param subArray 缺少整数的数组
  60.     */  
  61.    public static void getMissNumber(Integer array[], Integer subArray[]) {  
  62.        int xor = find_Miss_Number_By_XOR(array, subArray);  
  63.        System.out.println("异或的结果为" + xor);  
  64.        int mid = get_mid_By_xor_result(xor);  
  65.        System.out.println("最低位1的位置" + mid);  
  66.        // 数组A的元素为:  
  67.        System.out.println("数组A的元素为:");  
  68.        Integer[] array1 = divid_Array(array, mid);  
  69.        for (Integer e : array1) {  
  70.            System.out.print(e + "、");  
  71.        }  
  72.        // 数组B的元素为:  
  73.        System.out.println();  
  74.        System.out.println("数组B的元素为:");  
  75.        Integer[] array2 = divid_Array(subArray, mid);  
  76.        for (Integer e : array2) {  
  77.            System.out.print(e + "、");  
  78.        }  
  79.        System.out.println();  
  80.        System.out.println("缺少的第一个数为:");  
  81.        int solution1 = find_Miss_Number_By_XOR(array1, array2);  
  82.        System.out.println(solution1);  
  83.        System.out.println("缺少的第二个数为:");  
  84.        int solution2 = solution1 ^ xor;  
  85.        System.out.println(solution2);  
  86.  
  87.    }  
  88.  
  89.    public static void main(String[] args) {  
  90.        Integer array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12};  
  91.        Integer subArray[] = { 1, 2,  4, 5, 6, 7, 8, 9,11,12 };  
  92.        getMissNumber(array, subArray);  
  93.    }  
  94.  
  95. }  

(3)O(N)时间移动-排序

跟上面一样,实际上这种方法对于少了K个数的情况都能适用。

  1. import junit.framework.TestCase;  
  2.  
  3. public class Find_Miss_Numbers_By_Move extends TestCase {  
  4.  
  5.    // 1-N的自然数中,少了一个,找出这个数  
  6.    // 方法:将a[i]移动到a[a[i]],使得数组有序,然后找出空着的位置  
  7.    public void find_Miss_Number_By_Move(int array[], int n) {  
  8.  
  9.        int b[] = new int[n];  
  10.        for (int i : array) {  
  11.            b[i - 1] = i;  
  12.        }  
  13.  
  14.        System.out.print("数组的元素为:");  
  15.        for (int j : b) {  
  16.            if (j != 0) {  
  17.                System.out.print(j + "、");  
  18.            }  
  19.        }  
  20.          
  21.        System.out.println();  
  22.        System.out.print("缺少的数为:");  
  23.        for (int k = 0; k < b.length; k++) {  
  24.            if (b[k] == 0) {  
  25.                System.out.print(k + 1 + "、");  
  26.            }  
  27.        }  
  28.  
  29.    }  
  30.  
  31.    public void test() {  
  32.        int a[] = { 1, 2, 4, 7, 8, 9 };  
  33.        find_Miss_Number_By_Move(a, 9);  
  34.    }  
  35.  
  36. }  

(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的分到另一类。对每一类分别使用前一个问题的算法。

代码和异或找两个数一样,将测试数据该成即可

  1. public static void main(String[] args) {  
  2.    Integer array[] = { 1,1,2, 2, 3,3,4, 4, 5,5,};  
  3.    Integer subArray[] = {  1, 2,2, 3,4, 4, 5,5,};  
  4.    getMissNumber(array, subArray);  
  5. }

转载于:https://my.oschina.net/u/2260265/blog/384849

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值