Leetcode.260. 只出现一次的数字 III---精妙的位运算

260. 只出现一次的数字 III

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

示例 1:

输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:

输入:nums = [-1,0]
输出:[-1,0]
示例 3:

输入:nums = [0,1]
输出:[1,0]
提示:

2 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
除两个只出现一次的整数外,nums 中的其他数字都出现两次

题解:

方法一: 排序

  • 我们可以直接排序,接着通过排序后规整的数组可以很容易的求解。

方法二:位运算

为了实现线性时间复杂度常数空间复杂度,我们可以使用位运算来解决。
首先理解题目,假设我们要得到的两个数分别为num1num2,那么如果我们将数组内所有元素全部异或一遍,根据异或的性质以及题目的特殊性,我们可以知道最终得到的是num1和num2异或后的结果。
这个时候我们可能会想,我们能否使用位运算的那些运算符来反推出num1和num2究竟是谁?

  • 但是通过下面一个例子我们会发现我们无法直接通过num1和num2异或后的结果来直接反推出num1和num2:

例:(我们设num1和num2异或得到的结果为s)
设num1为 :0000101
设num2为 :0000100
那么s为 :0000001

我们通过后三位来判断,从最后一位开始,我们发现1与0不同,所以按照异或的性质返回的是1;接着二者均为0,所以返回0;接着二者均是1,所以返回的也是0。
这个时候我们发现,如果s化为2进制的某一位为1的话,我们还可以反推出num1和num2此时对应的位应为一个1一个0,但是如果s化为2进制的某一位为0的话,我们发现num1num2此时对应的位可以均为0也可以均为1,因此直接反推是不可行的!

既然根据s直接反推是不可行的,那么我们还有没有别的方式来得到num1num2呢?

  • 既然直接反推不行,那我们可以凭借s去筛选,即由于我们无法直接反推出结果,可以根据s的一些特性,来找到相互异或能合成这种特性的两个数num1num2
  • 再回到上面分析反推不可行的步骤,我们发现当s某一位为1时,我们是可以确定得到num1num2此位应该一个为0,一个为1的,因此我们可以以此性质作为我们的筛选条件去筛选!
  • 我们再次遍历nums数组,依据我们从s的二进制形式中选定的某一位(此位为1即可),依据nums中元素在此位是呈现0还是呈现1来分组,因此我们可以将nums中的数分为两组,接着让每组相互异或,由于此数组只有两个数是只出现一次的,其他的数均是两两出现,且相同的数必定会分在一组中,因此他们两两异或为0,两两异或为0,最后我们发现每组异或后的结果就是我们要的num1num2

代码:

class Solution {
    
    
    public int[] singleNumber(int[] nums) {
    
    
        // int[] temp = new int[2];    排序法
        // int index = 0;
        // Arrays.sort(nums);
        // int count = 1;
        // int res = nums[0];
        // for(int i=1;i<nums.length;i++){
    
    
        //     if(res==nums[i]){
    
    
        //         count++;
        //     }
        //     else{
    
    
        //         if(count==1){
    
    
        //             temp[index++] = res;
        //             count = 1;
        //             res = nums[i];
        //         }
        //         else{
    
    
        //             count = 1;
        //             res = nums[i];
        //         }
                
        //     }
        // }

        // if(count==1){
    
    
        //     temp[index++] = nums[nums.length-1];
        // }

        // return temp;

        int[] res = new int[2];
        int count = 0;
        for(int i=0;i<nums.length;i++){
    
    
            count^=nums[i];
        }

        int k = -1;
        for(int i=0;i<32;i++){
    
    
            if(((count>>i) & 1 )==1){
    
    
                k = i;
                break;
            }
        }

        for(int num : nums){
    
    
            if(((num >> k) & 1 )==1){
    
    
                res[0] ^= num;
            }

            else{
    
    
                res[1] ^= num;
            }
        }

        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/xiangguang_fight/article/details/121086031