56 - I. 数组中数字出现的次数

题目

一个整型数组 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]

思路

  • 题干:整型数组 nums 里除两个数字之外,其他数字都出现了两次
  • 如果某个数组中只有一个数字出现一次,其他都出现两次,那么所有元素求异或之后就是只有一次的数字,因为两个相同元素异或消除
  • 如果有两个数字a,b 只出现一次,那么就试图将他俩分到不同组里。首先对所有元素求异或,那么最终结果是x=a^b,我们从最低位分析,那么如果a和b在xi位相同,则xi=0,因此假设xj=1,那么在这一位上a和b不同,将其他元素按照这一位划分成两组,分别取异或
  1. 遍历异或:结果就是两个不同数字的异或值n
  2. 找出异或结果n中二进制第一个出现为1的位置:获得n最低位的1
  3. 分组:此位置与结果为0分为第一组,结果1分为第2组
    两个不同的元素此位置势必不同(因为n就是它两的异或结果m就是这两不同元素的异或为1的值,不同异或才为1,这两元素肯定只有一个与此位置相同,一个不同)势必分为不同组,相同势必分为同一组
    那两个不同的元素肯定会被分到不同组
  4. 对每一组异或最后剩余的就是只出现一次的元素
class Solution {
    public int[] singleNumbers(int[] nums) {
        //如果某个数组中只有一个数字出现一次,其他都出现两次,那么所有元素求异或之后就是只有一次的数字,因为两个相同元素异或消除
        //如果有两个数字a,b 只出现一次,那么就试图将他俩分到不同组里。首先对所有元素求异或,那么最终结果是x=a^b,我们从最低位分析,那么如果a和b在xi位相同,则xi=0,因此假设xj=1,那么在这一位上a和b不同,将其他元素按照这一位划分成两组,分别取异或。

        int x=0;
        int y=0;
        int m=1;
        int n=0;
        //遍历异或:结果就是两个不同数字的异或值n
        for(int num:nums){
            n^=num;
        }

        //找出异或结果n中二进制第一个出现为1的位置:获得n最低位的1(获取n的任意出现1的位置根据此位置分组)
        //while((n&m)!=1){}条件不成立,左移与出来的结果是0和非0(不是0和1)
        while((n&m)==0){
            m<<=1;
        }

        //分组:此位置与结果为0分为第一组,结果1分为第2组
        //两个不同的元素此位置必不同势必分为不同组,相同势必分为同一组
        //那两个不同的元素肯定会被分到不同组
        //对每一组异或最后剩余的就是只出现一次的元素
        /*
            举例假设:
            nums = [1,2,10,4,1,4,3,3]
            第一组:1,1,3,3, 10
            第二组:4,4, 2
        */
        for(int num:nums){
            if((m&num)==0){
                x^=num;
            }else{
                y^=num;
            }
        }

        return new int[]{x,y};

    }
}

复杂度

时间复杂度O(n): n为数组的长度
空间复杂度O(1): x,y,m,n都是常数阶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值