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.