只出现一次的数字系列

136.题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
一个数进行位运算有如下规律:

  • 0 ^ X = X
  • X ^ X = 0
  • X & ~X = 0
  • X & ~0 = X

代码:

class Solution {
    public int singleNumber(int[] nums) {
        int res=0;
        for(int i = 0;i<nums.length;i++){
            res ^= nums[i];
        }
        return res;
    }
}

最简单的也是最有效的方法,用异或来解决这个问题,因为相同的数字异或一定是0,因此重复数字异或后都是0了,0再和一个不为0的数字异或就是它本身了。

260.题目
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]

算法思路

此题看是简单,首最暴力的解法就是直接运用双层循环,此方法的时间复杂度为O(N^2),复杂度最高,效率低。

方法二: 是运用map来解题,此种方法时间复杂度为O(N),但引入了map,空间复杂度为O(N),消耗了额外的空间。大致思路是:将数组中每个数字作为键,出现的次数为值,然后将值为1对应的键加入结果集中即可。
代码如下:

class Solution {
    public int[] singleNumber(int[] nums) {
        if(nums.length <= 2) return nums;
        Map<Integer,Integer> map = new HashMap<>();
        int[] res = new int[2];
        for(int i = 0;i<nums.length;i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        int i = 0;
        for(int key: map.keySet()){
            if(map.get(key) == 1)
                res[i++] = key;
        }
        return res;
    }
}

方法三: 通过位运算来求解,使得时间复杂度和空间复杂度将到最低。

大致思路是:

  • 首先将数组中所有数字进行异或运算,相同两个数异或的0,所以最后得到的结果是两个不同数异或的结果;
  • 然后再将两个数进行分组,两个不同数字进行异或运算的结果中始终存在一个不相同的位,这样两个独特的数字分为不同组,相同位的数字分为一组;
  • 关键是如何找到这个分组位,由于所以数字异或的结果始终存在一个数字为1,所以我们就将最右边的非0那一位作为分组依据,0一组,1一组,这样两个独特元素就一定会分到不同组。

代码如下:


class Solution {
    public int[] singleNumbers(int[] nums) {
        if(nums.length <= 2) return nums;
        int sum = 0;
        for(int i = 0;i<nums.length;i++){
            sum ^= nums[i];   //将所有数字进行异或运算,得到的结果实质是不同的两个数异或结果
        }
        int xor = 1;//xor是用来分组的,
        while((xor&sum) == 0){//&运算,同为1结果位才是1,否则为0
            xor<<=1;        //由于两个同数字异或运算始终存在一个不同的位,找到最右边的第一个非0位
        }
        int[] res = new int[2];
        for(int i = 0;i<nums.length;i++){
            if((xor&nums[i]) == 0)
                res[0] ^= nums[i];
            else
                res[1] ^= nums[i];
        }
        return res;
    }
}

时间复杂度:O(N),只进行了两次单层循环
空间复杂度:O(1),没有引入额外的空间

137.题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2,2,3,2]
输出: 3

【笔记】网上大佬说过,如果能够设计一个状态转换电路,使得一个数出现3次后自动抵消为0,最后剩下只出现一次的数。
所以此题可以看做是电路题~

代码如下:

class Solution {
    public int singleNumber(int[] nums) {
        int a = 0,b = 0;
        for(int num: nums){
            a = (a^num)&~b;
            b = (b^num)&~a;
        }
        return a;
    }
}

那么就是很好解释上面的代码了。一开始a = 0, b = 0;

  • x第一次出现后,a = (a ^ x) & ~b的结果为 a = x, b = (b ^ x) & ~a的结果为此时因为a =
    x了,所以b = 0。
  • x第二次出现:a = (a ^ x) & ~b, a = (x ^ x) & ~0, a = 0; b = (b ^ x) & ~a
    化简, b = (0 ^ x) & ~0 ,b = x;
  • x第三次出现:a = (a ^ x) & ~b, a = (0 ^ x) & ~x ,a = 0; b = (b ^ x) & ~a
    化简, b = (x ^ x) & ~0 , b = 0;所以出现三次同一个数,a和b最终都变回了0.
  • 只出现一次的数,按照上面x第一次出现的规律可知a = x, b = 0;因此最后返回a.

参考
https://github.com/azl397985856/leetcode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值