剑指offer-数组中只出现1次的数字

题目描述

  • 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
  • 地址: 牛客链接

问题分析

  • 首先,若允许用额外空间的话,那么可以遍历数组,用set存储,如果当前元素,set中已经存在了,那么将该元素从set中移除;如果set中不存在当前元素,那么将之装入set中。最终,偶数次的元素全部移除,set中只有奇数次的元素。即为所求
  • 实际上,该题考察的不用额外空间,考察位运算的知识。
    • 若将这个数组求异或和,那么出现偶数次的元素相异或的结果为0,所以,最终的异或和 eor = a^b (假如只有a,b出现了奇数次)。那么对于eor 而言,必然至少有一位不为0,并且在那个位置上,a要是为1,b肯定为0;b要是为1,a肯定为0。
    • 所以,如果我们对于 eor 从右往左找到第一个不为0的位置,那么便可以区分a,b。 举例说明: a = 1110, b = 0100,那么 a^b = 1010。在从右往左数第2位上,xor不为0 ,a为1,b为0,这样便可以区分 a,b。
    • 问题又来了,如何得到一个二进制形式的最右边的1呢?
      • 遍历,与 1 << x 相与,直至结果不为0
      • eor & (-eor)
      • eor & (~eor + 1)(其实道理和上一个一样,因为位运算是对补码进行的,一个负数的补码,便是他对应正数的补码取反,加1)
      • 举例,对于a = 1110, b = 0100,那么 a^b = 1010。 eor & (~eor + 1) 的结果便是 0010。
    • 利用rightOne 便可以区分出 a 和 b,所以再次遍历数组,只有与 rightOne相与结果为1的才进行异或和,这样便淘汰了 a 或者 b 中的一个,而最终的异或肯定也是 a 或者 b。那么将 该异或和与 eor 相异或,那么结果是 两者中的另一个。 理由: a ^ b = c,那么 c ^ a = b

经验教训

  • 异或运算的性质(奇偶个数异或和性质)
  • 如何得到一个二进制形式的最右边的1呢: eor & (-eor) 或者 eor & (~eor + 1)
  • 该题可以引申出三个问题
    • 如果一个数组中,只有一个数字出现了奇数次,其他都出现了偶数次,那么如何得到该奇数次的数字
      • 求该数字的异或和,异或和即为所求
    • 如果一个数组中,有两个数字出现了奇数次,其他都出现了偶数次,那么如何得到该奇数次的数字
      • 方法同本题一样
    • 若数组中有一个数出现了一次,剩下的数出现了K次,如何求出出现了 1 次的数字
      • 将所有的数都转化为K进制形式,然后将所有K进制形式每一位相加,最后将每一位 对K取模,将每一位取模后的结果转换为十进制,即为所求。
      • 举例,7出现1次,4出现3次,那么 7的三进制形式为 21,4的三进制形式为 11。每一位累加结果为 2*1 + 1* 3 =5, 1*1 + 1*3 = 4。取模结果为 21,转为十进制 7
  • 相应位运算的题:
    • 剑指offer-二进制中1的个数
    • 剑指offer-数值的整数次方

代码实现

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        if (array == null || array.length == 0) {
            return;
        }
        int eor = 0;
        for (int i = 0; i < array.length; i++) {
            eor ^= array[i];
        }
        //int rightOne = eor & (~eor + 1);
        int rightOne = eor & (-eor);
        int firstValue = 0;
        for (int i = 0; i < array.length; i++) {
            if ((rightOne & array[i]) != 0) {
                firstValue ^= array[i];
            }
        }
        num1[0] = firstValue;
        num2[0] = firstValue ^ eor;
        return;
    }
}

你可能感兴趣的:(剑指offer,剑指offer(Java版),剑指offer,位运算,异或)