宝宝也能看懂的 leetcode 周赛 - 168 - 2

宝宝也能看懂的 leetcode 周赛 - 168 - 2

Hi 你们好,我是张小猪。欢迎来到『宝宝也能看懂』系列之 leetcode 题解。javascript

这里是第 168 期的第 2 题,也是题目列表中的第 1296 题 -- 『Divide Array in Sets of K Consecutive Numbers』java

题目描述

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into sets of k consecutive numbers
Return True if its possible otherwise return False.git

Example 1:github

Input: nums = [1,2,3,3,4,4,5,6], k = 4
Output: true
Explanation: Array can be divided into [1,2,3,4] and [3,4,5,6].

Example 2:shell

Input: nums = [3,2,1,2,3,4,3,4,5,9,10,11], k = 3
Output: true
Explanation: Array can be divided into [1,2,3] , [2,3,4] , [3,4,5] and [9,10,11].

Example 3:数组

Input: nums = [3,3,2,2,1,1], k = 3
Output: true

Example 4:微信

Input: nums = [1,2,3,4], k = 3
Output: false
Explanation: Each array should be divided in subarrays of size 3.

Constraints:数据结构

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
1 <= k <= nums.length

官方难度

MEDIUMide

解决思路

题目内容不长,是对于一个条件的判断。即尝试把输入数组进行拆分,拆分后每一段的长度都要为 k,而且每一段都要是连续的数字。post

看完题目以后,第一反应,数组长度能够先作判断,不能整除 k 就必定没法知足。接下来思路的大致方向就是,从最小或者最大开始,逐渐扫出符合要求的子段,而后剔除出去,直到遇到不符合要求的状况,或者剩下的内容为空为止。

直接方案

总体思路感受可行后,便开始具体施工。为了从一端开始,咱们首先对数组进行了排序。接下来最关键的就是对子段的剔除操做。这里可能有两种常见的方式:

  • 第一种方式,便是真正的执行这个剔除操做。这样作的话会受到底层数据结构的影响,例如若是是线性表,那么在中间进行部分移除操做会消耗蛮多的代价;而若是是链表,那么频繁的查询又是一个问题。而 JS 语言原生其实并无太多的内置数据结构。
  • 第二种方式,即用计数法来处理剔除行为。这种方式咱们不须要进行实际的剔除行为,只须要在 O(1) 的时间索引到目标,而后改变计数值便可。不过代价是须要 O(n) 的时间来初始化计数,以及占用额外的空间进行储存。

考虑到但愿总体时间能更加快,因此这里采用了第二种方式。因为数字的范围较大,[1, 10^9],因此使用了 Map 而不是固定长度的数组来储存计数。

const isPossibleDivide = (nums, k) => {
  if (nums.length % k !== 0) return false;
  const map = new Map();
  nums.sort((a, b) => a - b);
  for (const val of nums) {
    map.set(val, map.has(val) ? map.get(val) + 1 : 1);
  }
  for (let i = 0; i < nums.length; ++i) {
    const start = nums[i];
    const count = map.get(start);
    if (count === 0) continue;
    for (let i = 1; i < k; ++i) {
      const c = map.get(start + i)
      if (c === undefined || c < count) return false;
      map.set(start + i, c - count);
    }
    map.set(start, 0);
  }
  return true;
};

试了一下,Accepted,152ms。

优化

回头看代码,这个针对全数组的排序一直很让我在乎,由于单看它就会承担着比较大的额外性能负担,而它的收益仅仅是确保了咱们从数据的一端开始进行剔除操做。那么咱们是否有其余办法保证这件事情呢?

一个很是简单的思路是,对于咱们遍历到的数字,咱们尝试找到它前面最近的一个断点,这样咱们便知足了需求。而且随着一次次的剔除操做,这个查找断点的过程会更加的简单。

const isPossibleDivide = (nums, k) => {
  if (nums.length % k !== 0) return false;
  const map = new Map();
  for (const val of nums) {
    map.set(val, map.has(val) ? map.get(val) + 1 : 1);
  }
  for (let start of nums) {
    if (map.get(start) === 0) continue;
    while (map.get(--start) > 0);
    ++start;
    const count = map.get(start);
    for (let i = 1; i < k; ++i) {
      const c = map.get(start + i)
      if (c === undefined || c < count) return false;
      map.set(start + i, c - count);
    }
    map.set(start, 0);
  }
  return true;
};

这段代码最终跑到了 92ms,暂时 beats 100%。因而先凑合着这样了。

相关连接

个人微信公众号

相关文章
相关标签/搜索