LeetCode 260. Single Number III (只出现一次的数字 III)

目录

题目地址

原题目

方法一  遍历数组

解题过程

代码实现

复杂度分析

方法二  位运算

解题过程

代码实现

复杂度分析

方法三  哈希表


题目地址

260. Single Number III

原题目

 

方法一  遍历数组

看到 Single Number III,首先想到了之前做过的 Single Number,知道用位运算应该会更好的解决问题,可惜想了很久也没有头绪,只能先用笨方法挨个遍历去解决。

解题过程

  1. 先给数组排序,使得两个相同的数挨在一起
  2. 遍历数组,比较相邻的两个数 nums[i]  nums[i+1] 是否相同,相同则跳过这两个数继续遍历
  3. 如果这两个数不同,说明第一个数nums[i] 是Single Number,用left记录其位置
  4. 在比较第二个数nums[i+1] 与第三个数 nums[i+2]是否相同,不同则说明第二个数nums[i+1]也是单独出现的数,返回这两个数结束遍历
  5. 相同则说明第二个数nums[i+1] 与第三个数 nums[i+2]不是Single Number,跳过继续遍历

代码实现

class Solution {
    public int[] singleNumber(int[] nums) {
        Arrays.sort(nums);
        int left =-1;
        for(int i=0;i<nums.length;i+=2){
            // 遍历到了最后一个元素,则它一定是 Single Number
            if(i >=nums.length-1 || nums[i]!=nums[i+1]){
                // 判断是否已经找到了第一个 Single Number
                if(left==-1){
                    left = i;
                    // 一直到了最后第二个元素还没有找到任何一个 Single Number,
                    // 则最后两个元素一定都是 Single Number
                    if(i >=nums.length-2 || nums[i+1]!=nums[i+2]){
                        return new int[]{nums[i],nums[i+1]};
                    }
                }else{
                    return new int[]{nums[left],nums[i]};
                }
                // 只有第一个元素nums[i]是 Single Number 时,连续跳过三个元素
                i++;
            }
        }
        return new int[2];
    }
}

复杂度分析

时间复杂度:O(n*logn) 快速排序( +O(n) 的时间遍历输入数组)。
空间复杂度:快速排序 O(logn)。

方法二  位运算

然后找到了位运算的解法 >> 数组中不重复的两个元素   

                                         >> 异或运算

解题过程

  1. 全部元素异或消掉出现两次的数字,则异或结果diff 等于两个Single Number的异或结果
  2. 两个不相等的元素至少有一位存在位级不同,通过diff &= -diff 得到出 diff 最右侧不为 0 的位(即lowbit(diff) ),也就是不存在重复的两个元素在位级表示上的最低位,利用这一位就可以将两个元素区分开来。
  3. 将所有元素分为两组,一组在此位上是 0,另一组在此位上是 1,而且出现两次的相同数字肯定在同一组
  4. 同一组的元素再异或消掉同出现两次的数字,剩下的就是各自的Single Number
  5. 返回两组各自的Single Number

lowbit函数来源于树状数组,其含义是得到该数的二进制从右往左第一个非0位所表示的10进制数。
lowbit 的常用计算方法是 x&(-x) ,x&(-x) 是保留位中最右边的1 ,且将其余的1 设为 0 的方法。
-x = (~x)+1 // 即 补码等于反码加一

例如:

6=0110

变为反码后为 0001

再加一为 0010 即它的补码

lowbit(6) = 0110 & 0010 = 0010 = 2

代码实现

public int[] singleNumber(int[] nums) {
        int diff = 0;
        // 全部元素异或消掉出现两次的数字
        for (int num : nums) {
            diff ^= num; 
        }
        // 寻找lowbit(diff),这两个数在此位上必然一个是 1 ,一个是 0
        diff &= -diff;  
        int[] ret = new int[2];
        for (int num : nums) {
            // 将所有元素分为两组,一组在此位上是 0,另一组在此位上是 1,而且出现两次的数字肯定在同一组
            // 同一组的元素再异或消掉同出现两次的数字,剩下的就是只出现一次的
            if ((num & diff) == 0){
                ret[0] ^= num; 
            } else { 
                ret[1] ^= num; 
            }
        }
        return ret;
    }

复杂度分析

  • 时间复杂度:O(N)的时间遍历输入数组。

  • 空间复杂度:O(1)。

浏览LeetCode提交后的执行用时分布图表发现优化的位运算代码:

class Solution {
    public int[] singleNumber(int[] nums) {
        if(nums==null || nums.length<2)
            return new int[0];
        // 所有元素异或
        int diff=0;
        for(int num:nums) diff^=num;

        // 求lowbit(diff)
        int mask=diff&(-diff);

        int x=0;
        for(int num:nums){
            // lowbit位上为1的Single Number
            if((num & mask)!=0)
                x^=num;
        }
        // 所有元素异或结果与lowbit位上为1的Single Number异或得到另一个Single Number
        return new int[]{x,diff^x};
    }
}

方法三  哈希表

力扣的一个题解,也是一种比较复杂的方法

来源:只出现一次的数字 III

public int[] singleNumber(int[] nums) {
    Map<Integer, Integer> hashmap = new HashMap();
    for (int n : nums)
      hashmap.put(n, hashmap.getOrDefault(n, 0) + 1);

    int[] output = new int[2];
    int idx = 0;
    for (Map.Entry<Integer, Integer> item : hashmap.entrySet())
      if (item.getValue() == 1) output[idx++] = item.getKey();

    return output;
  }

复杂度分析

  • 时间复杂度:O(N)。
  • 空间复杂度:O(N),哈希表所使用的空间。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值