前言
本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
给大家推荐一个力扣上的专栏,leetbook 初级算法:
对应的还有中级算法和高级算法,里面不仅分了专题还能够不断的循序渐进,多次练习肯定能够帮助大家在笔试上大获全胜!
下面这段话出自这个专栏的概述:
清晰明确,只要大家掌握方法,勤加练习,不再畏惧算法,定能收获不错的offer,提高自己的coding能力!话不多说直接进入主题:
题目1
题目来源LeetCode :136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
思路
第一反应是可以通过哈希的思想来完成,通过 hashmap 来进行处理,但是其实这样势必就会用到额外的空间,题目说明最好能够不占用额外的空间就完成。
于是更换思路,可以使用位运算的中的异或运算,两数相同为0,两数相异为1,
在非空数组中,除了落单的那个数,其余的全部都是成双成对的,那么相同的两个数异或运算后结果就位0,
而最后落单的那个数与0进行异或运算,就自然得到它本身。
所以最后的运算结果就是落单的那个数。
数的异或运算规则如下:
a ^ 1 = -a; a ^ 0 = a; a ^ a = 0;
代码
// 方法一,哈希表
class Solution {
public int singleNumber(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
Integer count = map.get(nums[i]);
count = count == null ? 1 : ++count;
map.put(nums[i],count);
}
for(int i = 0; i < nums.length; i++){
if(map.get(nums[i]) == 1){
return nums[i];
}
}
return -1;
}
}
// 方法二,位运算——异或运算
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int num : nums){
res ^= num;
}
return res;
}
}
老规矩,我们再来看看相关标签和相似题目:
同样我们来深入挖掘一下后面这两道相似的题目:
题目2
题目来源:137. 只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
思路
这一题跟上一题比起来无非就是配对的数字多了一个,但是落单的数字仍然只有一个,那么使用哈希的思路仍然是可以一举拿下,而且直接复制上一题的代码就能ac。
但是考虑到空间复杂度,所以我们仍可以使用位运算:
将我们数组中的每一个数字的二进制每一位相加,然后对其每一位的和取余:
如下图:(选自袁记菜馆)
之所以这么做是因为,题目说到除了落单的数字以外,所有的其他数字都是三个为一组的,那么每一位的1的个数无非有两种情况:
为3的倍数,(全为出现三次的数),或 3 的倍数 + 1(包含出现一次的数)。这 3 的倍数 +1的情况也就是落单的那个数的对应一位。
代码
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int i = 0; i < 32; i++){
int count = 0;
for (int j = 0; j < nums.length; j++) {
//先将数右移,并求出最后一位为 1 的个数
if ((nums[j] >> i & 1) == 1) {
count++;
}
}
//找到某一位取余为 1 的数,并左移,为了将这一位循环结束后移至原位
if (count % 3 != 0) {
res = res | 1 << i;
}
}
return res;
}
}