寶寶也能看懂的 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%。因而先湊合着這樣了。

相關連接

個人微信公衆號

相關文章
相關標籤/搜索