🔗 🟡 1296. Divide Array in Sets of K Consecutive Numbers 1490

tags: Weekly Contest 168 貪心(Greedy) 雜湊表(Hash Table) 排序(Sorting)

題意

給定一個整數陣列 numsnums 和一個正整數 kk,檢查是否可以將此陣列分成 kk 個連續數字的集合。

如果可以,返回 True\rm{True}。否則,返回 False\rm{False}

約束條件

  • 1knums.length1051 \leq k \leq nums.length \leq 10^5
  • 1nums[i]1091 \leq nums[i] \leq 10^9

思路:貪心(Greedy) + 雜湊表(Hash Table) + 排序(Sorting)

基於貪心思路,由於我們需要讓每組集合都是 kk 個連續的數字,因此 numsnums 中的最小數字必定是一組集合的起點。假設陣列中最小的數字為 xx,則我們需要將 x,x+1,x+2,...,x+k1x, x+1, x+2, ..., x+k-1kk 個數字分到同一組集合中。

因此我們可以使用一個 雜湊表(Hash Table) cntcnt 來記錄每個數字的數量,並且對其鍵值進行排序(或是使用 C++map)來確保我們能夠從最小的數字開始檢查連續性。

具體步驟如下:

  1. 使用 Countermap 來統計每個數字的數量,並依照鍵值排序。
  2. 遍歷排序後的鍵值,對每個數字進行檢查,如果該個數字的數量為 00,則跳過。否則則嘗試組成以這個數字為起點的連續組合,共需要組成 cnt[k]cnt[k] 組連續數字的集合。
  3. 如果某個數字無法組成連續組合(即該組內的某個數字的剩餘數量小於需要的數量),則返回 False\rm{False}
  4. 每次成功組成一個連續數字的集合後,減少相應數字的數量。
  5. 如果所有數字都能成功組成連續數字的集合,則返回 True\rm{True}

複雜度分析

  • 時間複雜度:O(nlogn)\mathcal{O}(n \log n),其中 nn 為陣列的長度。
    • 統計每個數字的出現次數需要 O(n)\mathcal{O}(n) 時間。
    • 排序需要 O(mlogm)\mathcal{O}(m \log m) 時間,其中 mm 為不同數字的個數,可以視為 O(n)\mathcal{O}(n)
    • 每個數字最多會被遍歷一次,因此處理所有數字的過程需要 O(m+n)\mathcal{O}(m + n) 時間,可以視為 O(n)\mathcal{O}(n)
  • 空間複雜度:O(n)\mathcal{O}(n) ,忽略排序的空間複雜度。
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def isPossibleDivide(self, nums: List[int], k: int) -> bool:
cnt = Counter(nums)
for x in sorted(cnt.keys()):
if cnt[x] == 0:
continue
need = cnt[x]
for i in range(k):
if cnt[x+i] < need:
return False
cnt[x+i] -= need
return True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
bool isPossibleDivide(vector<int>& nums, int k) {
map<int, int> cnt; // ordered map
for (int x : nums) cnt[x]++;
for (auto it = cnt.begin(); it != cnt.end(); it++) {
if (it->second == 0) continue;
int need = it->second;
for (int i = 0; i < k; i++) {
if (cnt[it->first + i] < need) return false;
cnt[it->first + i] -= need;
}
}
return true;
}
};

類題


寫在最後

Cover photo is generated by @ゴリラの素材屋さん, thanks for their work!