只出现一次的数字--位运算leetcode

只出现一次的数字--位运算leetcode

愿题目是这样的:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,4]
输出: 4

算法1,直接使用暴力的话算法复杂度要达到O(n2)

算法2,如果不用暴力,用额外空间空间两个Set集合来分别几下出现一次和两次的数字,setOne,setTwo分别几下已经出现了一次和两次的数字,大家都知道set里面的元素是唯一的,不能重复,遍历数组,先从setOne里查找有没有这个数,如果有,就把这个数移到setTwo,如果没有就从setTwo里面查找,如果有的话就把setTwo的这个数移除掉,如果setTwo都没有,就把这个数添加到setOne里面,这样遍历完数组就setTwo的size等于0,setOne的size就等于1, 就是出现了一次的数字,算法复杂度O(nlgon)

算法3,不用额外的空间,直接把数组用sort函数排序一遍,遍历数组找到出现次数为1的数字,算法复杂度也是为O(nlogn)。

算法4,有一种很牛逼的算法—位运算,就是题目要求的不用额外空间,算法复杂度为O(n)的一种算法。为了更容易理解这个位运算是怎么一回事,先来看看这道题的一个简易版,题目就该一个字就是:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4

这道题与上一道提唯一的不同就是其元素只出现两次,运用位运算,把数字都转变成二进制,如下。
在这里插入图片描述由上面的数据可以看出,出现一次的数字其实就是每列1的个数为奇数对应的二进制位置的为1,看第一个例子,第4位0是偶数,所以对应的是0, 第3位为1,是奇数,对应的位为1,第2位为2,对应的1进制为0, 第四位为2,对应的也是为0,所以最后出现一次的数字为0100,就是4了。
那怎么找出对应的位为奇数的所有二进制位呢?有一个 ^ 运算,也就是异或运算,还是以上面的例子为例
在这里插入图片描述没错就是这么简单,只要一直异或下去,最终结果就是只出现一次的数字,代码如下

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

好的,回到第一题,其余元素都出现了三次,找出只出现一次的数字,那这道题怎么算?还是先把数字变成二进制,看看有没有什么规律,如下
在这里插入图片描述可以看出来,如果对应的列的数目 % 3 = 1,那答案对应二进制的位就为1,否则为0,我们可以用三个变量来记录状态
one记录出现数目 % 3 = 1 的位
two记录出现数目 % 3 = 2 的位
three记录出现数目 % 3 = 0 的位
直接上代码进行解释

class Solution {
    public int singleNumber(int[] nums) {
        int one = 0, two = 0, three = 0;
        
        three = -1; //刚开始所有的位对应的位数目都为0,所以three的所有位都为1
        
        for(int i = 0; i < nums.length; i++) {
            int temp = three;
            
            three &= ~(three & nums[i]); // three & nums[i] 就是出现数目 % 3 =0 后在出现一次的位,也就是出现数目 % 3 = 1,要把这些位从three状态中去除,移到one中
            three |= two & nums[i];   //two & nums[i]  就是出现数目 % 3 = 2 后再出现一次的位,也就是变成了出现数目 % 3 =0,把这些位移到three状态中
            
            two &= ~(two & nums[i]);//two状态要移去那些状态已经从two变成three的位
            two |= one & nums[i]; //one & nums[i]  就是出现数目 % 3 = 1 后再出现一次的位,也就是变成了出现数目 % 3 = 2,把这些位移到two状态中
            
            
            one &= ~(one & nums[i]); // one状态要移去那些状态已经从one变成two的位
            one |= temp & nums[i]; // temp & nums[i]  就是出现数目 % 3 = 0 后再出现一次的位,也就是变成了出现数目 % 3 = 1,把这些位移到one状态中
            
        }
        return one;
    }
}

从代码就可以看出来,其实就是一个状态的循环转换,就算把题目变成其余元素都是出现4,5,6,7次,找出只出现一次的数字,再加几行代码就行了。
(希望本文对你有所帮助)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值