Talentd
FleetCode
Make and follow sheets.Sign in

Divide Array in Sets of K Consecutive Numbers solution and explanation

MediumArrayGreedyHash TableSorting14 min read59.2% acceptanceUpdated 9 Apr 2026
846. Hand of Straights | 1296. Divide Array in Sets of K Consecutive Numbers | Sorting | MapAryan Mittal · 20:41 · 8K viewsWatch on YouTube
9 more videos on this problem
  1. 846. Hand of Straights | 1296. Divide Array in Sets of K Consecutive Numbers | Sorting | MapAryan Mittal · 20:41 · 8K views
  2. Leetcode 1296: Divide Array in Sets of K Consecutive NumbersAlgorithms Casts · 17:11 · 4K views
  3. 花花酱 LeetCode 1296. Divide Array in Sets of K Consecutive Numbers - 刷题找工作 EP287Hua Hua · 20:21 · 2K views
  4. Leetcode 1296 - Divide Array in Sets of K Consecutive Numbers (JAVA, Solution Explain!)LC Bear · 8:06 · 2K views
  5. 1296. Divide Array in Sets of K Consecutive Numbers | Leetcode MediumJOY of LIFE · 34:00 · 1K views
  6. 1296. Divide Array in Sets of K Consecutive Numbers | English | MediumSimple Code - 단순코드 · 7:08 · 629 views
  7. LeetCode 1296. [Medium] Divide Array in Sets of K Consecutive NumbersFrancesco Manicardi · 4:59 · 518 views
  8. leetcode 1296 Divide Array in Sets of K Consecutive NumbersGradCode · 29:15 · 342 views
  9. 846. Hand of Straights | 1296. Divide Array in Sets of K Consecutive Numbers | Hash Table | SortingLeet's Code · 16:25 · 167 views
  10. Leetcode 1296 - Divide Array in Sets of K Consecutive NumbersDebugWithNazia · 6:00 · 83 views

The problem

Given an array of integers nums and a positive integer k, check whether it is possible to divide this array into sets of k consecutive numbers.

Return true if it is possible. Otherwise, return false.

Example 1:

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:

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 = [1,2,3,4], k = 3
Output: false
Explanation: Each array should be divided in subarrays of size 3.

Constraints:

  • 1 <= k <= nums.length <= 105
  • 1 <= nums[i] <= 109
Note: This question is the same as 846: https://leetcode.com/problems/hand-of-straights/

Approach overview

Problem Overview: You receive an integer array and a value k. The task is to check whether the array can be split into groups of size k such that every group forms k consecutive numbers. Each element must be used exactly once. If all numbers can be arranged into these consecutive groups, return true; otherwise return false.

Approach 1: Dynamic Programming with Frequency Map (O(n log n) time, O(n) space)

Start by sorting the array so numbers are processed in increasing order. Build a frequency map using a hash table to track how many times each number appears. Iterate through the sorted numbers, and whenever a number still has remaining frequency, attempt to form a consecutive group starting from that value. For every value x, decrement counts for x, x+1, ..., x+k-1. If any required number is missing or its count becomes negative, forming valid groups is impossible. This approach behaves like dynamic programming because each decision (starting a sequence at a number) updates state stored in the frequency map. Sorting ensures smaller sequences are resolved before larger ones, avoiding conflicts.

This method relies heavily on efficient lookups in a hash table and ordered traversal enabled by sorting. It works well for general inputs and is the most common solution pattern used in interviews.

Approach 2: Space Optimized Dynamic Programming / Greedy Extension (O(n log n) time, O(n) space)

Instead of repeatedly checking each sequence independently, track how many sequences are currently expecting the next number. After sorting the array, maintain two hash maps: one for remaining frequencies and another that records how many sequences end at a specific number. When processing a value x, you first try to extend an existing sequence ending at x-1. If such a sequence exists, decrement its count and extend it to end at x. If no sequence can be extended, attempt to start a new group x, x+1, ..., x+k-1 by verifying that the next k-1 numbers exist in the frequency map.

This greedy strategy minimizes redundant checks because sequences grow naturally instead of being rebuilt from scratch. It still depends on ordered processing through arrays and leverages ideas from greedy algorithms. Memory usage is slightly more efficient because the algorithm tracks only active sequences instead of repeatedly validating entire ranges.

Recommended for interviews: The frequency-map greedy approach is what most interviewers expect. Sorting plus a hash map clearly demonstrates understanding of array ordering and counting techniques. Showing the optimized greedy extension approach signals stronger algorithmic thinking because it avoids unnecessary sequence reconstruction while maintaining the same asymptotic complexity.

Approach 1: Dynamic Programming Approach

This approach involves breaking the problem into overlapping subproblems, solving each just once, and storing their solutions - optimal substructure. It is efficient for problems with optimal substructure and overlapping subproblems.

def solve(n):
    dp = [0] * (n + 1)
    if n > 0:
        dp[1] = 1
    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

n = 10
print(solve(n))
public class Solution {
    public static int solve(int n) {
        if (n <= 1) return n;
        int[] dp = new int[n+1];
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }

    public static void main(String[] args) {
        int n = 10;
        System.out.println(solve(n));
    }
}
#include <iostream>
#include <vector>

int solve(int n) {
    std::vector<int> dp(n + 1, 0);
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; ++i) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

int main() {
    int n = 10;
    std::cout << solve(n);
    return 0;
}
function solve(n) {
    let dp = Array(n + 1).fill(0);
    if (n > 0) dp[1] = 1;
    for (let i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

let n = 10;
console.log(solve(n));
#include <stdio.h>
#define MAX_SIZE 1000

int dp[MAX_SIZE];

int solve(int n) {
    if (n <= 1) return n;
    if (dp[n] != -1) return dp[n];
    return dp[n] = solve(n-1) + solve(n-2);
}

int main() {
    int n = 10;
    for (int i = 0; i <= n; ++i) dp[i] = -1;
    printf("%d", solve(n));
    return 0;
}
using System;

class Program {
    public static int Solve(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 0;
        if (n > 0) { dp[1] = 1; }
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }

    static void Main() {
        int n = 10;
        Console.WriteLine(Solve(n));
    }
}

In Python, this solution utilizes a list to dynamically store results, iterating to fill each state up to the nth term.

Complexity

Time Complexity: O(n), Space Complexity: O(n)

Try this approach in the editor

Approach 2: Space Optimized Dynamic Programming

This approach is an optimization of the dynamic programming approach, aiming to reduce space complexity by storing only the last two results needed to compute the current state instead of the entire array.

def solve(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

n = 10
print(solve(n))
public class Solution {
    public static int solve(int n) {
        if (n <= 1) return n;
        int a = 0, b = 1;
        for (int i = 2; i <= n; i++) {
            int temp = a;
            a = b;
            b = temp + b;
        }
        return b;
    }

    public static void main(String[] args) {
        int n = 10;
        System.out.println(solve(n));
    }
}
#include <iostream>

int solve(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        int temp = a;
        a = b;
        b = temp + b;
    }
    return b;
}

int main() {
    int n = 10;
    std::cout << solve(n);
    return 0;
}
function solve(n) {
    if (n <= 1) return n;
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
        [a, b] = [b, a + b];
    }
    return b;
}

console.log(solve(10));
#include <stdio.h>

int solve(int n) {
    if (n <= 1) return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

int main() {
    int n = 10;
    printf("%d", solve(n));
    return 0;
}
using System;

class Program {
    public static int Solve(int n) {
        if (n <= 1) return n;
        int a = 0, b = 1;
        for (int i = 2; i <= n; i++) {
            int temp = a;
            a = b;
            b = temp + b;
        }
        return b;
    }

    static void Main() {
        Console.WriteLine(Solve(10));
    }
}

This Python solution optimizes space usage by only maintaining the last two states, avoiding an array altogether.

Complexity

Time Complexity: O(n), Space Complexity: O(1)

Try this approach in the editor

Approach 3: Hash Table + Sorting

First, we check if the length of the array nums is divisible by k. If it is not divisible, it means the array cannot be divided into subarrays of length k, and we return false directly.

Next, we use a hash table cnt to count the occurrences of each number in the array nums, and then we sort the array nums.

After sorting, we iterate through the array nums, and for each number x, if cnt[x] is not 0, we enumerate each number y from x to x + k - 1. If cnt[y] is 0, it means we cannot divide the array into subarrays of length k, and we return false directly. Otherwise, we decrement cnt[y] by 1.

After the loop, if no issues were encountered, it means we can divide the array into subarrays of length k, and we return true.

The time complexity is O(n times log n), and the space complexity is O(n). Here, n is the length of the array nums.

function isPossibleDivide(nums: number[], k: number): boolean {
    if (nums.length % k !== 0) {
        return false;
    }
    const cnt = new Map<number, number>();
    for (const x of nums) {
        cnt.set(x, (cnt.get(x) || 0) + 1);
    }
    nums.sort((a, b) => a - b);
    for (const x of nums) {
        if (cnt.get(x)! > 0) {
            for (let y = x; y < x + k; y++) {
                if ((cnt.get(y) || 0) === 0) {
                    return false;
                }
                cnt.set(y, cnt.get(y)! - 1);
            }
        }
    }
    return true;
}
func isPossibleDivide(nums []int, k int) bool {
	if len(nums)%k != 0 {
		return false
	}
	sort.Ints(nums)
	cnt := map[int]int{}
	for _, x := range nums {
		cnt[x]++
	}
	for _, x := range nums {
		if cnt[x] > 0 {
			for y := x; y < x+k; y++ {
				if cnt[y] == 0 {
					return false
				}
				cnt[y]--
			}
		}
	}
	return true
}
Try this approach in the editor

Approach 4: Ordered Set

Similar to Solution 1, we first check if the length of the array nums is divisible by k. If it is not divisible, it means the array cannot be divided into subarrays of length k, and we return false directly.

Next, we use an ordered set sd to count the occurrences of each number in the array nums.

Then, we repeatedly extract the smallest value x from the ordered set and enumerate each number y from x to x + k - 1. If these numbers all appear in the ordered set with non-zero occurrences, we decrement their occurrence count by 1. If the occurrence count becomes 0 after the decrement, we remove the number from the ordered set; otherwise, it means we cannot divide the array into subarrays of length k, and we return false.

If we can successfully divide the array into subarrays of length k, we return true after completing the traversal.

The time complexity is O(n times log n), and the space complexity is O(n). Here, n is the length of the array nums.

function isPossibleDivide(nums: number[], k: number): boolean {
    if (nums.length % k !== 0) {
        return false;
    }
    const tm = new TreeMap<number, number>();
    for (const x of nums) {
        tm.set(x, (tm.get(x) || 0) + 1);
    }
    while (tm.size()) {
        const x = tm.first()![0];
        for (let y = x; y < x + k; ++y) {
            if (!tm.has(y)) {
                return false;
            }
            if (tm.get(y)! === 1) {
                tm.delete(y);
            } else {
                tm.set(y, tm.get(y)! - 1);
            }
        }
    }
    return true;
}

type Compare<T> = (lhs: T, rhs: T) => number;

class RBTreeNode<T = number> {
    data: T;
    count: number;
    left: RBTreeNode<T> | null;
    right: RBTreeNode<T> | null;
    parent: RBTreeNode<T> | null;
    color: number;
    constructor(data: T) {
        this.data = data;
        this.left = this.right = this.parent = null;
        this.color = 0;
        this.count = 1;
    }

    sibling(): RBTreeNode<T> | null {
        if (!this.parent) return null; // sibling null if no parent
        return this.isOnLeft() ? this.parent.right : this.parent.left;
    }

    isOnLeft(): boolean {
        return this === this.parent!.left;
    }

    hasRedChild(): boolean {
        return (
            Boolean(this.left && this.left.color === 0) ||
            Boolean(this.right && this.right.color === 0)
        );
    }
}

class RBTree<T> {
    root: RBTreeNode<T> | null;
    lt: (l: T, r: T) => boolean;
    constructor(compare: Compare<T> = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) {
        this.root = null;
        this.lt = (l: T, r: T) => compare(l, r) < 0;
    }

    rotateLeft(pt: RBTreeNode<T>): void {
        const right = pt.right!;
        pt.right = right.left;

        if (pt.right) pt.right.parent = pt;
        right.parent = pt.parent;

        if (!pt.parent) this.root = right;
        else if (pt === pt.parent.left) pt.parent.left = right;
        else pt.parent.right = right;

        right.left = pt;
        pt.parent = right;
    }

    rotateRight(pt: RBTreeNode<T>): void {
        const left = pt.left!;
        pt.left = left.right;

        if (pt.left) pt.left.parent = pt;
        left.parent = pt.parent;

        if (!pt.parent) this.root = left;
        else if (pt === pt.parent.left) pt.parent.left = left;
        else pt.parent.right = left;

        left.right = pt;
        pt.parent = left;
    }

    swapColor(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
        const tmp = p1.color;
        p1.color = p2.color;
        p2.color = tmp;
    }

    swapData(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
        const tmp = p1.data;
        p1.data = p2.data;
        p2.data = tmp;
    }

    fixAfterInsert(pt: RBTreeNode<T>): void {
        let parent = null;
        let grandParent = null;

        while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) {
            parent = pt.parent;
            grandParent = pt.parent.parent;

            /*  Case : A
                Parent of pt is left child of Grand-parent of pt */
            if (parent === grandParent?.left) {
                const uncle = grandParent.right;

                /* Case : 1
                   The uncle of pt is also red
                   Only Recoloring required */
                if (uncle && uncle.color === 0) {
                    grandParent.color = 0;
                    parent.color = 1;
                    uncle.color = 1;
                    pt = grandParent;
                } else {
                    /* Case : 2
                       pt is right child of its parent
                       Left-rotation required */
                    if (pt === parent.right) {
                        this.rotateLeft(parent);
                        pt = parent;
                        parent = pt.parent;
                    }

                    /* Case : 3
                       pt is left child of its parent
                       Right-rotation required */
                    this.rotateRight(grandParent);
                    this.swapColor(parent!, grandParent);
                    pt = parent!;
                }
            } else {
                /* Case : B
               Parent of pt is right child of Grand-parent of pt */
                const uncle = grandParent!.left;

                /*  Case : 1
                    The uncle of pt is also red
                    Only Recoloring required */
                if (uncle != null && uncle.color === 0) {
                    grandParent!.color = 0;
                    parent.color = 1;
                    uncle.color = 1;
                    pt = grandParent!;
                } else {
                    /* Case : 2
                       pt is left child of its parent
                       Right-rotation required */
                    if (pt === parent.left) {
                        this.rotateRight(parent);
                        pt = parent;
                        parent = pt.parent;
                    }

                    /* Case : 3
                       pt is right child of its parent
                       Left-rotation required */
                    this.rotateLeft(grandParent!);
                    this.swapColor(parent!, grandParent!);
                    pt = parent!;
                }
            }
        }
        this.root!.color = 1;
    }

    delete(val: T): boolean {
        const node = this.find(val);
        if (!node) return false;
        node.count--;
        if (!node.count) this.deleteNode(node);
        return true;
    }

    deleteAll(val: T): boolean {
        const node = this.find(val);
        if (!node) return false;
        this.deleteNode(node);
        return true;
    }

    deleteNode(v: RBTreeNode<T>): void {
        const u = BSTreplace(v);

        // True when u and v are both black
        const uvBlack = (u === null || u.color === 1) && v.color === 1;
        const parent = v.parent!;

        if (!u) {
            // u is null therefore v is leaf
            if (v === this.root) this.root = null;
            // v is root, making root null
            else {
                if (uvBlack) {
                    // u and v both black
                    // v is leaf, fix double black at v
                    this.fixDoubleBlack(v);
                } else {
                    // u or v is red
                    if (v.sibling()) {
                        // sibling is not null, make it red"
                        v.sibling()!.color = 0;
                    }
                }
                // delete v from the tree
                if (v.isOnLeft()) parent.left = null;
                else parent.right = null;
            }
            return;
        }

        if (!v.left || !v.right) {
            // v has 1 child
            if (v === this.root) {
                // v is root, assign the value of u to v, and delete u
                v.data = u.data;
                v.left = v.right = null;
            } else {
                // Detach v from tree and move u up
                if (v.isOnLeft()) parent.left = u;
                else parent.right = u;
                u.parent = parent;
                if (uvBlack) this.fixDoubleBlack(u);
                // u and v both black, fix double black at u
                else u.color = 1; // u or v red, color u black
            }
            return;
        }

        // v has 2 children, swap data with successor and recurse
        this.swapData(u, v);
        this.deleteNode(u);

        // find node that replaces a deleted node in BST
        function BSTreplace(x: RBTreeNode<T>): RBTreeNode<T> | null {
            // when node have 2 children
            if (x.left && x.right) return successor(x.right);
            // when leaf
            if (!x.left && !x.right) return null;
            // when single child
            return x.left ?? x.right;
        }
        // find node that do not have a left child
        // in the subtree of the given node
        function successor(x: RBTreeNode<T>): RBTreeNode<T> {
            let temp = x;
            while (temp.left) temp = temp.left;
            return temp;
        }
    }

    fixDoubleBlack(x: RBTreeNode<T>): void {
        if (x === this.root) return; // Reached root

        const sibling = x.sibling();
        const parent = x.parent!;
        if (!sibling) {
            // No sibiling, double black pushed up
            this.fixDoubleBlack(parent);
        } else {
            if (sibling.color === 0) {
                // Sibling red
                parent.color = 0;
                sibling.color = 1;
                if (sibling.isOnLeft()) this.rotateRight(parent);
                // left case
                else this.rotateLeft(parent); // right case
                this.fixDoubleBlack(x);
            } else {
                // Sibling black
                if (sibling.hasRedChild()) {
                    // at least 1 red children
                    if (sibling.left && sibling.left.color === 0) {
                        if (sibling.isOnLeft()) {
                            // left left
                            sibling.left.color = sibling.color;
                            sibling.color = parent.color;
                            this.rotateRight(parent);
                        } else {
                            // right left
                            sibling.left.color = parent.color;
                            this.rotateRight(sibling);
                            this.rotateLeft(parent);
                        }
                    } else {
                        if (sibling.isOnLeft()) {
                            // left right
                            sibling.right!.color = parent.color;
                            this.rotateLeft(sibling);
                            this.rotateRight(parent);
                        } else {
                            // right right
                            sibling.right!.color = sibling.color;
                            sibling.color = parent.color;
                            this.rotateLeft(parent);
                        }
                    }
                    parent.color = 1;
                } else {
                    // 2 black children
                    sibling.color = 0;
                    if (parent.color === 1) this.fixDoubleBlack(parent);
                    else parent.color = 1;
                }
            }
        }
    }

    insert(data: T): boolean {
        // search for a position to insert
        let parent = this.root;
        while (parent) {
            if (this.lt(data, parent.data)) {
                if (!parent.left) break;
                else parent = parent.left;
            } else if (this.lt(parent.data, data)) {
                if (!parent.right) break;
                else parent = parent.right;
            } else break;
        }

        // insert node into parent
        const node = new RBTreeNode(data);
        if (!parent) this.root = node;
        else if (this.lt(node.data, parent.data)) parent.left = node;
        else if (this.lt(parent.data, node.data)) parent.right = node;
        else {
            parent.count++;
            return false;
        }
        node.parent = parent;
        this.fixAfterInsert(node);
        return true;
    }

    search(predicate: (val: T) => boolean, direction: 'left' | 'right'): T | undefined {
        let p = this.root;
        let result = null;
        while (p) {
            if (predicate(p.data)) {
                result = p;
                p = p[direction];
            } else {
                p = p[direction === 'left' ? 'right' : 'left'];
            }
        }
        return result?.data;
    }

    find(data: T): RBTreeNode<T> | null {
        let p = this.root;
        while (p) {
            if (this.lt(data, p.data)) {
                p = p.left;
            } else if (this.lt(p.data, data)) {
                p = p.right;
            } else break;
        }
        return p ?? null;
    }

    count(data: T): number {
        const node = this.find(data);
        return node ? node.count : 0;
    }

    *inOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
        if (!root) return;
        for (const v of this.inOrder(root.left!)) yield v;
        yield root.data;
        for (const v of this.inOrder(root.right!)) yield v;
    }

    *reverseInOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
        if (!root) return;
        for (const v of this.reverseInOrder(root.right!)) yield v;
        yield root.data;
        for (const v of this.reverseInOrder(root.left!)) yield v;
    }
}

class TreeMap<K = number, V = unknown> {
    _size: number;
    tree: RBTree<K>;
    map: Map<K, V> = new Map();
    compare: Compare<K>;
    constructor(
        collection: Array<[K, V]> | Compare<K> = [],
        compare: Compare<K> = (l: K, r: K) => (l < r ? -1 : l > r ? 1 : 0),
    ) {
        if (typeof collection === 'function') {
            compare = collection;
            collection = [];
        }
        this._size = 0;
        this.compare = compare;
        this.tree = new RBTree(compare);
        for (const [key, val] of collection) this.set(key, val);
    }

    size(): number {
        return this._size;
    }

    has(key: K): boolean {
        return !!this.tree.find(key);
    }

    get(key: K): V | undefined {
        return this.map.get(key);
    }

    set(key: K, val: V): boolean {
        const successful = this.tree.insert(key);
        this._size += successful ? 1 : 0;
        this.map.set(key, val);
        return successful;
    }

    delete(key: K): boolean {
        const deleted = this.tree.deleteAll(key);
        this._size -= deleted ? 1 : 0;
        return deleted;
    }

    ceil(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left'));
    }

    floor(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right'));
    }

    higher(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left'));
    }

    lower(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right'));
    }

    first(): [K, V] | undefined {
        return this.toKeyValue(this.tree.inOrder().next().value);
    }

    last(): [K, V] | undefined {
        return this.toKeyValue(this.tree.reverseInOrder().next().value);
    }

    shift(): [K, V] | undefined {
        const first = this.first();
        if (first === undefined) return undefined;
        this.delete(first[0]);
        return first;
    }

    pop(): [K, V] | undefined {
        const last = this.last();
        if (last === undefined) return undefined;
        this.delete(last[0]);
        return last;
    }

    toKeyValue(key: K): [K, V];
    toKeyValue(key: undefined): undefined;
    toKeyValue(key: K | undefined): [K, V] | undefined;
    toKeyValue(key: K | undefined): [K, V] | undefined {
        return key != null ? [key, this.map.get(key)!] : undefined;
    }

    *[Symbol.iterator](): Generator<[K, V], void, void> {
        for (const key of this.keys()) yield this.toKeyValue(key);
    }

    *keys(): Generator<K, void, void> {
        for (const key of this.tree.inOrder()) yield key;
    }

    *values(): Generator<V, undefined, void> {
        for (const key of this.keys()) yield this.map.get(key)!;
        return undefined;
    }

    *rkeys(): Generator<K, undefined, void> {
        for (const key of this.tree.reverseInOrder()) yield key;
        return undefined;
    }

    *rvalues(): Generator<V, undefined, void> {
        for (const key of this.rkeys()) yield this.map.get(key)!;
        return undefined;
    }
}
func isPossibleDivide(nums []int, k int) bool {
	if len(nums)%k != 0 {
		return false
	}
	tm := treemap.NewWithIntComparator()
	for _, x := range nums {
		if v, ok := tm.Get(x); ok {
			tm.Put(x, v.(int)+1)
		} else {
			tm.Put(x, 1)
		}
	}
	for !tm.Empty() {
		x, _ := tm.Min()
		for y := x.(int); y < x.(int)+k; y++ {
			if v, ok := tm.Get(y); ok {
				if v.(int) == 1 {
					tm.Remove(y)
				} else {
					tm.Put(y, v.(int)-1)
				}
			} else {
				return false
			}
		}
	}
	return true
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming Approach

Time Complexity: O(n), Space Complexity: O(n)

Space Optimized Dynamic Programming

Time Complexity: O(n), Space Complexity: O(1)

Hash Table + Sorting–
Ordered Set–

Complexity analysis

ApproachTimeSpaceWhen to Use
Dynamic Programming with Frequency MapO(n log n)O(n)General solution. Easy to reason about using sorting and hash counts.
Space Optimized DP / Greedy ExtensionO(n log n)O(n)When you want fewer redundant checks by extending existing sequences greedily.

Frequently asked questions

Is Divide Array in Sets of K Consecutive Numbers easy or hard?

The problem is generally classified as Medium difficulty on coding platforms. The logic is straightforward once you recognize the greedy pattern with frequency counting, but handling duplicates and ensuring correct sequence formation can be tricky without sorting and proper hash map management.

Divide Array in Sets of K Consecutive Numbers Python/Java solution

In both Python and Java, the typical implementation uses a map or dictionary to store frequencies and processes numbers in sorted order. For each smallest available value, attempt to build a group of k consecutive numbers by decrementing counts in the map. If any required value is missing, return false; otherwise continue until all elements are used.

How to solve Divide Array in Sets of K Consecutive Numbers in O(n)?

Pure O(n) time is difficult because ordering of numbers is required to build consecutive groups. Most correct solutions first sort the array, resulting in O(n log n) complexity. After sorting, a hash map or greedy extension technique ensures each element is processed efficiently while forming valid sequences.

What is the best approach for Divide Array in Sets of K Consecutive Numbers?

The most reliable approach sorts the array and uses a hash map to track frequencies. For each smallest unused number, try forming a sequence of length k by checking x, x+1, ..., x+k-1. Decrement counts as you build groups. This greedy counting technique runs in O(n log n) time due to sorting and O(n) space for the frequency map.

Is Divide Array in Sets of K Consecutive Numbers asked at Google/Amazon/Meta?

Variants of this problem appear in interviews at large tech companies including Amazon, Google, and Meta. The question tests understanding of hash maps, greedy grouping, and handling ordered sequences in arrays. Interviewers typically expect a frequency-map greedy solution with clear reasoning about consecutive ranges.

What data structure is used in Divide Array in Sets of K Consecutive Numbers?

The primary data structure is a hash table (frequency map) that stores how many times each number appears. Sorting the array ensures numbers are processed in increasing order. Some optimized solutions also maintain an additional map tracking sequences that end at a specific number.

What is the time complexity of Divide Array in Sets of K Consecutive Numbers?

The optimal solution runs in O(n log n) time because the array must be sorted before forming groups. After sorting, each element is processed a constant number of times using hash map lookups. Space complexity is O(n) for storing element frequencies and sequence tracking.

Comments

Now write it yourselfClose this page and solve Divide Array in Sets of K Consecutive Numbers in the editor, without looking.
Practise Divide Array in Sets of K Consecutive Numbers