2

Hi 大家好,我是张小猪。欢迎来到『宝宝也能看懂』系列特别篇 - 30-Day LeetCoding Challenge。这是一个 leetcode 官方的小活动。可以在官网看到,从 4 月 1 号开始,每天官方会选出一道题,在 24 小时内完成即可获得一点小奖励。

这里是 4 月 7 号的题,是一道新题,不在之前的题目列表中 -- 『Counting Elements』

题目描述

Given an integer array arr, count element x such that x + 1 is also in arr.
给定一个整数数组 arr,对于其中的任何一个 x,如果 x + 1 也在数组中,则计数结果加一。

If there're duplicates in arr, count them seperately.
如果存在多个相同的 x,我们需要分别进行计数。

示例 1:

输入: arr = [1,2,3]
输出: 2
解释: 1 and 2 are counted cause 2 and 3 are in arr.

示例 2:

输入: arr = [1,1,3,3,5,5,7,7]
输出: 0
解释: No numbers are counted, cause there's no 2, 4, 6, or 8 in arr.

示例 3:

输入: arr = [1,3,2,3,5,0]
输出: 3
解释: 0, 1 and 2 are counted cause 1, 2 and 3 are in arr.

示例 4:

输入: arr = [1,1,2,2]
输出: 2
解释: Two 1s are counted cause 2 is in arr.

数据范围:

  • 1 <= arr.length <= 1000
  • 0 <= arr[i] <= 1000

官方难度

UNKNOWN(我猜 EASY)

解决思路

虽然是一道新题,不过看起来也很直白。我们可以通过记录出现过的数字,然后再判断是否存在 +1 的数字,来轻松 AC。

直接方案

思路如上,代码如下:

const countElements = (arr) => {
  const counts = new Uint8Array(1001);
  let ret = 0;
  for (const val of arr) counts[val] = 1;
  for (const val of arr) counts[val + 1] && ++ret;
  return ret;
};

换个思路

上面的代码里,我们是通过两轮遍历来完成计算。那是否可以只用一轮遍历来实现呢?

如果只用一轮遍历,就意味着在遍历过程中我们不会有完整的数字存在与否的信息,也就意味着我们需要结合当前的数据来逐渐推出结果。不过我们很容易发现,在遍历的过程中其实只有两种情况:

  • 如果存在 +1 的数字,则我们需要对当前数字进行计数,并且当前数字将被消耗掉,不能在以后继续用作累计。
  • 如果存在 -1 的数字,则我们需要对之前所有的 -1 数字进行计数,并且把它们消耗掉。

所以我们可以添加一种特殊的标识,用以指示当前数字是存在的,但是已经被完全消耗掉了。从而完成整个计数过程。具体代码如下:

const countElements = (arr) => {
  const counts = new Uint16Array(1001);
  const MAGIC = 2000;
  let ret = 0;
  for (const val of arr) {
    if (counts[val - 1] && counts[val - 1] !== MAGIC) {
      ret += counts[val - 1];
      counts[val - 1] = MAGIC;
    }
    counts[val + 1] ? (++ret, (counts[val] = MAGIC)) : ++counts[val];
  }
  return ret;
};

优化

其实这里可能有的小伙伴已经发现了,在上面的思路中,如果一个数字第一次完成了在情况 2 中的处理,那么它将不会再遇到情况 2。所以我们可以省去相关的判断,其实也就是省去这个特殊的标识,直接就能完成计数的过程。具体代码如下:

const countElements = (arr) => {
  const counts = new Uint16Array(1001);
  let ret = 0;
  for (const val of arr) {
    counts[val - 1] && !counts[val] && (ret += counts[val - 1]);
    counts[val + 1] && ++ret;
    ++counts[val];
  }
  return ret;
};

总结

虽然是一道新题目,不过想 AC 还是非常容易的,因为思路是非常明显的。只不过从两轮遍历到一轮遍历,可能稍微需要思考一下。

这里其实很有可能小伙伴们会直接跳过第二种方案,想到第三种方案。不过小猪还是把它写了出来,当做中间思路的过渡。希望能帮到有需要的小伙伴。

如果觉得不错的话,记得『三连』哦。小猪爱你们哟~

相关链接

qrcode_green.jpeg


张小猪粉鼻子
2.9k 声望407 粉丝

一只粉色的小猪,喜欢写写代码,看看电影,玩玩游戏社恐,不太会讲话永远跟不上热点