整数:求数组Nums中只出现一次的数

题目

数组Nums, 只有一个数只出现一次,其余的数都会出现3次。
例如:
输入: Nums[2,2,3,2]。
输出:3

考察知识点

位运算

  • 异或
    异或有一个特性,即相同的数之间异或后,结果是0。有一个题目与此题目类似。它是数组中除了一个数字只出现一次,其余数字都出现两次。 对于那个题目,我们可以采用数字之间异或的特性。数组内的整数,依次异或,最后运算的结果,即为数组中只出现一次的数。例如:
    输入:nums[1, 0, 2, 1, 0]
    输出:2
    二进制形式的位运算推到过程如下:
    两两异或:1 ^ 0 ^ 2 ^ 1 ^ 0
   0001
^  0000
=  0001

   0001
^  0010
=  0011

   0011
^  0001
=  0010
    
  0010
^ 0000 
= 0010  => 最终结果为数组中只出现一次的整数2
  • 右移
    有符号整数规定如下:
  1. 正数的二进制位右移1位,最左边补上0。
  2. 负数的二进制位右移1位,则最左边补上1.
  • 左移
    与上面同理,有符号整数里,
  1. 正数的二进制位左移1位,最右边补上0。
  2. 负数的二进制位左移1位,最右边补上0.
//负数左移1位的代码如下:
    int i = -1;
    int b = i << 1; //左移1位,最右位补0
    System.out.println(" b = " + b); //b = -2
  • 与(&1)
    整数&1,即可把整数除最右边的数位,都设置为0. 可用来取整数二进制形式中最右边的一位。

解题思路

    public int getNumber(int[] nums) {
//1. 数组sumBits用来保存数组nums中所有整数的二进制形式中第i个数位之和。
        int[] sumBits = new int[32];
        for(int num: nums) {
            for (int i = 0; i < 32; i++) {
                sumBits[i] += ((num >> (31 - i)) & 1); //2.用来得到整数num的二进制形式中从左数起第i个数位。
            }
        }
        //此时每个数位的和已经求出来了
        //我们再来计算每一个数位整数3的余数,来确认最终数字二进制形式每一个位结果。
        int result = 0;
        for(int i = 0; i <32;  i++) {
            result = (result << 1 )+ sumBits[i] %3; //左移一位,补上最新结果。
        }
        return result;
    }

总结

求数组中只出现一次的整数,主要利用一个整数是由32个0或1组成,而数组中重复出现3次的整数,其二进制形式中相同的数位相加的和,整除3后的余数是0,而只出现一次的数,整除3取余后,刚好是余数1。那么这样只出现一次的任意第i个数位可以由数组中所有数字的第i个数位之和推算出来。

  1. 取整数num的二进制形式中的从左到右算,数位i的值时,可以套用公式:
(num >> (32 - i) ) & 1
  1. 任何整数异或它自己结果是0。

扩展:举一反三

假设数组Nums中,只有一个整数出现m次,其他数都出现n次,求唯一出现m次的那个数字。m不能被n整除。

解答思路

数组中,对于出现n次的整数,它的二进制形式中的数位i的和一定能被n整除。那么出现m次数的数字的数位i一定是0,否则一定是1.
(参考上面题目思路,解决同类型问题。余数是出现m次的数字相位之和, 我们只要把余数除以m,等于1,如果余数是0
,则所求数字的相位i为0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值