Leetcode Single Number 全家桶

Single Number
题目描述
给出一个数组,数组中只有一个数字只出现了一次,其余数字都出现了两次,求这个只出现了一次的数字
解题思路
这里我们用到位运算异或^的特性a ^ a = 00 ^ a = a
不难看出仅需要O(n)的时间内就可以解出答案,代码如下:

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

Single Number II
题目描述
给出一个数组,但是每个数都出现了3次,仅一个数出现1次,求出现1次的这个数
参考链接

  1. leetcode 之 Single Number II
  2. Detailed explanation and generalization of the bitwise operation method for single numbers - LeetCode Discuss
    解题思路
    这里我们仍然用位运算^ 但是与上一个题略有不同
    在介绍位运算的具体做法之前,我们首先给出一种更加朴素的想法:统计每一位1出现的次数并取余。在不考虑答案时,其余数字都出现了三次,因此对每一位的统计结果也必然是3的倍数,显然对于统计结果求余就是出现一次的数字,也就是答案
    s t a t i c s [ i ] = 3 ⋅ n + ( ( a n s ≫ i ) & 1 ) statics[i] = 3\cdot n + ((ans \gg i) \& 1) statics[i]=3n+((ansi)&1)
    但是用一个32位int来记录一个不大于4的数有点大才小用,实际上2bit足以,所以我们把两个int竖起来用
    在这里插入图片描述

这样就极大的节省了空间,但是怎么实现求余的操作呢?这时我们不妨列出真值表

highlowinputhigh_outlow_out
00101
00000
01110
01001
10100
10010

可知
l o w _ o u t = h i g h ˉ ⋅ l o w ˉ ⋅ i n p u t + h i g h ˉ ⋅ l o w ⋅ i n p u t ˉ h i g h _ o u t = h i g h ˉ ⋅ l o w ⋅ i n p u t + h i g h ⋅ l o w ˉ ⋅ i n p u t ˉ low\_out = \bar{high}\cdot \bar{low} \cdot input + \bar{high}\cdot low \cdot \bar{input} \\ high\_out = \bar{high}\cdot low \cdot input + high \cdot \bar{low}\cdot \bar{input} low_out=highˉlowˉinput+highˉlowinputˉhigh_out=highˉlowinput+highlowˉinputˉ
h i g h _ o u t high\_out high_out转为依赖 l o w _ o u t low\_out low_out则有

highlow_outinputhigh_out
0000
0110
0011
0100
1010
1001


h i g h _ o u t = h i g h ˉ ⋅ l o w _ o u t ˉ ⋅ i n p u t + h i g h ⋅ l o w _ o u t ˉ ⋅ i n p u t ˉ high\_out = \bar{high}\cdot \bar{low\_out}\cdot input + high\cdot\bar{low\_out}\cdot\bar{input} high_out=highˉlow_outˉinput+highlow_outˉinputˉ
根据上述逻辑表达式,我们就可以完成 00 → 01 → 10 → 00 00\to01\to10\to00 00011000的逻辑循环,也就是取余操作。因为重复数字贡献的次数取余后为0,仅剩余答案的位信息,所以返回低位计数器x1就是所求答案

class Solution {
    public int singleNumber(int[] nums) {
        int x1 = 0,x2 = 0;
        for(int i=0;i<nums.length;i++){
            x1 = ~x2 & (x1 ^ nums[i]);
            x2 = ~x1 & (x2 ^ nums[i]);
        }
        return x1;
    }
}

Single Number III
题目描述
给一个数组,所以元素都出现了两次,有两个元素出现了一次,求这两个元素
解题思路
这个题是最为接近Single Number的。同样采用位操作^,对数组异或,得到两个出现一次元素x y的异或值sum = x ^ y 。根据异或的运算规则我们知道,某位为1,则表示x和y在该位处值不相同,知道了这个就可以把数组分成两组:一组在该位为0;另一组为1。由于其他元素都出现了两次,所以在加入分组是也是成对的加入(因为重复出现的元素是相同的),这样就转换为了两个Single Number问题的求解

class Solution {
    public int[] singleNumber(int[] nums) {
        int sum = 0;
        int len = nums.length;
        for(int i=0;i<len;i++){
            sum = sum ^ nums[i];
        }
        
        int dif = sum & -sum;
        int[] ans = new int[2];
        for(int ele : nums){
            if( (ele & dif) == 0)
                ans[0] ^= ele;
            else
                ans[1] ^= ele;
        }
        return ans;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值