三连杀拿下【只出现一次的数字】|刷题打卡

172 阅读2分钟

前言

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

给大家推荐一个力扣上的专栏,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

之所以这么做是因为,题目说到除了落单的数字以外,所有的其他数字都是三个为一组的,那么每一位的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;       
    }
}