只出现一次的数字--位运算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次,找出只出现一次的数字,再加几行代码就行了。
(希望本文对你有所帮助)