Python 1296.py
class Solution:
def isPossibleDivide(self, nums: List[int], k: int) -> bool:
if k == 1:
return True
if k > len(nums):
return False
freq = defaultdict(int)
for n in nums:
freq[n] += 1
keys = list(sorted(freq))
for f in keys:
need = freq[f]
if need > 0:
for i in range(f, f + k):
freq[i] -= need
if freq[i] < 0:
return False
return True
Difficulty
and has an acceptance rate of about 59% (200k+ submissions).
Typical patterns to solve this problem:
- Array
- Greedy
- Hash Table
- Sorting
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation solves the problem of determining whether an integer array can be partitioned into groups of size k such that each group consists of consecutive numbers. The algorithm follows a greedy strategy: it processes the numbers in ascending order, repeatedly extracting the smallest available value and attempting to build a consecutive block of length k starting from that value. If at any point the required count for a number becomes negative, the function concludes that a valid division is impossible and returns False; otherwise it returns True after all numbers have been successfully allocated.
Core Logic
Early exit conditions
The function begins with two simple checks that handle trivial scenarios without further processing.
if k == 1:
return True
if k > len(nums):
return False
When k equals 1, any array can be divided into single-element groups, so the answer is always True. Conversely, if the desired group size exceeds the total number of elements, forming even a single group is impossible, and the function immediately returns False.
Frequency map construction
A frequency dictionary is built to count occurrences of each distinct integer in the input list.
freq = defaultdict(int)
for n in nums:
freq[n] += 1
Using defaultdict(int) ensures that missing keys default to zero, simplifying the increment operation. This map enables constant-time look-ups for the remaining count of any value during the greedy extraction phase.
Sorted key iteration
The distinct numbers are sorted to guarantee that the algorithm always starts building groups from the smallest available element.
keys = list(sorted(freq))
for f in keys:
need = freq[f]
if need > 0:
# attempt to form a block starting at f
Iterating over the sorted keys preserves the natural order required for consecutive groups. The variable need captures how many copies of the current smallest number still need to be placed into groups.
Forming consecutive blocks
If need is positive, the algorithm attempts to create need groups that each begin with the current number f. For each integer in the interval [f, f + k), the required count is decreased by need.
for i in range(f, f + k):
freq[i] -= need
if freq[i] < 0:
return False
The inner loop walks through the k consecutive values that would complete each group. Subtracting need from each entry reflects the consumption of those numbers across all groups that start at f. The moment any frequency drops below zero, it indicates that the current number does not have enough copies to satisfy all pending groups, and the function aborts with False.
Successful completion
If the outer loop finishes without triggering a negative frequency, every number has been allocated to a valid consecutive block, and the function returns True.
return True
Edge Cases Handled
The implementation explicitly addresses several boundary conditions:
- Single-element groups - The
k == 1shortcut ensures immediate success without further computation. - Group size larger than the array - The
k > len(nums)guard prevents unnecessary work when division is mathematically impossible. - Repeated values - By storing counts in
freq, the algorithm correctly handles inputs where the same number appears multiple times, allowing it to form several groups that share the same starting value. - Missing intermediate numbers - During block formation, if any integer in the required consecutive range is absent (frequency defaults to zero), the subtraction will produce a negative count, causing an early
Falsereturn. This captures cases where the input lacks the necessary continuity. - Negative numbers - The sorting and frequency map operate on the raw integer values, so the algorithm works equally well for negative numbers, as long as they appear in the input list.
Performance Optimizations
Several design choices contribute to efficient execution:
- Early termination - The two initial checks eliminate work for trivial inputs, saving time on large arrays that meet those conditions.
- Use of
defaultdict- This avoids explicit existence checks when updating frequencies, reducing overhead in the counting loop. - Single pass over sorted keys - By iterating over each distinct value exactly once and performing the necessary adjustments in place, the algorithm avoids repeated scanning of the entire array.
- In-place frequency updates - Modifying the
freqdictionary directly eliminates the need for auxiliary data structures to track used elements, conserving memory and keeping the inner loop tight. - Immediate failure detection - The moment a negative frequency is observed, the function returns, preventing further unnecessary iterations.
These aspects together ensure that the solution scales well with the size of the input while maintaining clear and concise logic.
ELI5 - Simplified Explanation
Imagine a big pile of numbered building blocks. The goal is to organize these blocks into small groups so that every group has exactly the right number of blocks in a row. For example, if the special number is three, a group could have a four, a five, and a six.
The first part of the code checks if the groups are small enough to be possible. If you are asked to make a group that is bigger than the number of blocks you have, that cannot happen. If you only need groups of one, that is always fine because every block can be its own group.
Next, the code looks at all the blocks and counts how many of each number are in the pile. It keeps these counts in a list so it knows exactly what is available. It sorts the numbers from smallest to largest to make everything neat and tidy.
Finally, the code goes through each number one by one. If it sees a number that needs to be part of a group, it tries to find the next numbers that go with it to finish the set. It subtracts one from the count of those blocks as it builds the groups. If it ever finds that it does not have enough blocks to finish a set, it knows the task cannot be done.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||
|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||
| www | AlgoMonster | C++ | Python | Java | TS | |||
| WalkCCC | C++ | Python | Java | |||||
| leetcode.ca | C++ | Python | Java | Go | TS | |||
| MemoryLimitExceeded | C++ | Python | ||||||
| Progiez | C++ | Python | Java | |||||
| github | BigEggStudy | C# | ||||||
| black-shadows | C++ | |||||||
| charles-wangkai | Java | |||||||
| fsq | C++ | |||||||
| Garvit244 | Python | |||||||
| halfrost | Go | |||||||
| JoshCrozier | JS | |||||||
| keineahnung2345 | C++ | |||||||
| lkwq007 | Python | |||||||
| shreymehul | C# | |||||||
| suxrobGM | C# | |||||||
| varunu28 | Java | |||||||
| wisdompeak | C++ | |||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||
| WalkCCC | C++, Python, Java | |||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | |||||||
| MemoryLimitExceeded | Python, C++ | |||||||
| Progiez | C++, Java, Python | |||||||
| BigEggStudy | C# | |||||||
| black-shadows | C++, C++ | |||||||
| charles-wangkai | Java | |||||||
| fsq | C++ | |||||||
| Garvit244 | Python | |||||||
| halfrost | Go | |||||||
| JoshCrozier | JavaScript | |||||||
| keineahnung2345 | C++ | |||||||
| lkwq007 | Python | |||||||
| shreymehul | C# | |||||||
| suxrobGM | C# | |||||||
| varunu28 | Java | |||||||
| wisdompeak | C++ |
Videos
Similar Problems
- problem 659: Split Array into Consecutive Subsequences
Solved On The Same Day
- problem 1360: Number of Days Between Two Dates
- problem 1576: Replace All ?'s to Avoid Consecutive Repeating Characters
- problem 1716: Calculate Money in Leetcode Bank
- problem 2409: Count Days Spent Together
- problem 2515: Shortest Distance to Target String in a Circular Array
- problem 2644: Find the Maximum Divisibility Score
- problem 1110: Delete Nodes And Return Forest
- problem 1443: Minimum Time to Collect All Apples in a Tree
- problem 1669: Merge In Between Linked Lists
- problem 458: Poor Pigs
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality