【剑指 Offer 56】 - I. 数组中数字出现的次数

发布时间 2023-04-12 11:11:35作者: 梦想是能睡八小时的猪

【题目】

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

 

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

 

限制:

    2 <= nums.length <= 10000

 

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof

【思路】

1.将数组所有值异或可以得到目标两个数的异或结果z=x^y(出现两次的数异或后都为0了,不会影响结果)

2.接下来就是把这两个数分离 如果这两个数异或后的位数是1,表示两个数异或结果不同,如果是0,表示相同。

用0001,0010,0100...这类数来依次判断异或后的数最低的一个1位在哪里 最后用m来保存,这个数可以在原数组中区分x和y,

这两个数在这一位不同,那么我们只要遍历原数组中每一个数,【与】这个数就能分开。

3.使用m在原数组中遍历与,结果为0的为一组(包含若干出现2次的数和一个出现1次的数),1的为一组(包含若干出现2次的数和一个出现1次的数)

4.然后在这两组数分别异或,就得到了x和y(出现2次的数异或后为0,不影响结果,只有出现1次数会有影响)

 
【代码】
class Solution {
    public int[] singleNumbers(int[] nums) {
        // 将数组所有值异或可以得到目标两个数的异或结果x^y
        // 接下来就是把这两个数分离 如果这两个数异或后的位数是1,表示两个数异或结果不同,如果是0,表示相同
        // 首先用0001,0010,0100...这类数来依次判断异或后的数最低的一个1位在哪里 最后用m来保存,这个数可以在原数组中区分x和y
        // 使用m在原数组中遍历,结果为0的为一组(包含若干出现2次的数和一个出现1次的数),1的为一组(包含若干出现2次的数和一个出现1次的数)
        // 然后在这两组数分别异或,就得到了x和y(出现2次的数异或后为0,不影响结果,只有出现1次数会有影响)
        int z = 0;
        int x = 0;
        int y = 0;
        for(int num:nums){
            z = num^z;
        }
        int m = 1;
        while((z&m)==0){
            m = m<<1;
        }
        for(int num:nums){
            if((num&m)==0){
               x^=num;
            }else{
               y^=num;
            }
        }

        return new int[]{x,y};
    }
}