面试题56:数组中数字出现的次数

题目:

数组中只出现一次的两个数字。

一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度O(n),空间复杂度O(1)。

分析:

例如,输入数组{2,4,3,6,3,2,5,5},因为只有4和6只出现了一次,其他数字出现了两次,所以输出4和6。

先考虑这样一个问题,如果这个数组中,只有一个数组出现1次,其余数字都出现了2次,怎么找到这个出现了1次的数字呢?这时候,可以想到异或运算的一个性质:任何一个数字异或自己都等于0。也就是从头到尾异或一遍的结果,就是只出现1次的数字了,因为出现两次的数字都变成0了。

再回到原来的问题上,如果尝试将原数组分成两部分,将出现1次的两个数字分到两部分中,就可以使用上面的方法把出现1次的这两个数找出来了。此时,将数组里的数字依次异或,这个操作的效果等价于将只出现1次的两个数字做异或。这个结果值一定不为0,那么,这个数的二进制形式,一定至少存在一个1,在结果值的二进制中找到右数的第一个1。那么就根据这个特点,将原数组划分成两部分。那些成对出现的数字一定被划分到了一组中,出现1次的两个数字也可以被划分到两个部分。

解法:

package com.wsy;

public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{2, 4, 3, 6, 3, 2, 5, 5};
        int first = 0, sum = 0, length = array.length;
        for (int i = 0; i < length; i++) {
            sum ^= array[i];
        }
        int right = sum & (~sum + 1);// 找出最右边的1
        for (int i = 0; i < length; i++) {
            if ((array[i] & right) != 0) {// 根据right中的1对数组中的数据分组
                first ^= array[i];
            }
        }
        System.out.println("出现一次的数字是:" + first + "," + (sum ^ first));
    }
}

题目:

数组中唯一只出现一次的数字。

在一个数组中除一个数字之外,其他数字都出现了三次。请找出那个只出现了一次的数字。

分析:

上面的异或思路,在这里不能用了,于是换一个思路。如果一个数字出现了3次,那么它的二进制表示的每一位也出现了3次,将所有出现3次的数字的二进制表示的每一位加起来,每一位的和都可以被3整除。

于是,我们将数组中的数字都用二进制表示出来,将它们按位相加,如果某一位的和能被3整除,那么只出现1次的这个数的二进制表示中,这一位是0,否则,这一位是1。那么就得到了这个只出现1次数字的二进制表示,也就得到了这个只出现一次的值。

解法:

package com.wsy;

public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 6, 7, 7, 7};
        int[] bits = new int[32];
        int length = array.length;
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < 32; j++) {
                bits[j] += array[i] >> (31 - j) & 1;// 结果:bits[0]是最高位,bits[31]是最低位
            }
        }
        int result = 0;
        for (int i = 0; i < 32; i++) {
            result <<= 1;
            result += bits[i] % 3;
        }
        System.out.println("出现一次的数字是:" + result);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值