# 0/1 Knapsack vs Unbounded Knapsack: Key Differences in JavaScript

> Explore the core differences between 0/1 Knapsack and Unbounded Knapsack algorithms in JavaScript. Learn when to use dynamic programming for optimal solutions or a greedy approach for unlimited items.

- Repository: [Oleksii Trekhleb/javascript-algorithms](https://github.com/trekhleb/javascript-algorithms)
- Tags: deep-dive
- Published: 2026-02-24

---

**The 0/1 Knapsack uses dynamic programming to guarantee an optimal solution when each item can be selected at most once, while the Unbounded Knapsack uses a greedy approach that allows unlimited copies of each item but only provides a heuristic solution.**

The `trekhleb/javascript-algorithms` repository implements both variants in [`src/algorithms/sets/knapsack-problem/Knapsack.js`](https://github.com/trekhleb/javascript-algorithms/blob/main/src/algorithms/sets/knapsack-problem/Knapsack.js), providing a direct comparison between dynamic programming and greedy strategies for solving knapsack problems.

## Core Algorithmic Differences

### 0/1 Knapsack: Dynamic Programming Approach

The **0/1 Knapsack** restricts each item to **at most one selection** (0 or 1 times). The implementation uses a classic dynamic programming matrix where `knapsackMatrix[i][w]` stores the maximum achievable value using the first `i+1` items with a weight limit of `w`.

The algorithm fills this matrix row-by-row using the recurrence relation that compares including versus excluding the current item. After computing the optimal value, it backtracks through the matrix to reconstruct the exact set of selected items.

### Unbounded Knapsack: Greedy Approach

The **Unbounded Knapsack** allows **unlimited copies** of each item. Rather than using dynamic programming, this implementation employs a **greedy strategy** that sorts items by value and value-per-weight ratio, then iteratively fills the remaining capacity with as many copies of the highest-value items as possible.

The algorithm respects the `itemsInStock` property to cap quantities when stock limits exist, setting the `quantity` field on each selected item to indicate how many copies were included.

## Implementation Details in Knapsack.js

Both algorithms live in the `Knapsack` class within [`src/algorithms/sets/knapsack-problem/Knapsack.js`](https://github.com/trekhleb/javascript-algorithms/blob/main/src/algorithms/sets/knapsack-problem/Knapsack.js), exposed through distinct methods:

- `solveZeroOneKnapsackProblem()` – Implements the DP approach
- `solveUnboundedKnapsackProblem()` – Implements the greedy approach

### 0/1 Knapsack Matrix Construction

The DP solution initializes a 2D array with dimensions based on the number of items and weight limit:

```javascript
// Lines 73-78: Matrix initialization
const knapsackMatrix = Array(numberOfRows).fill(null)
  .map(() => Array(numberOfColumns + 1).fill(null));

```

The recurrence logic (lines 94-116) determines whether to include the current item based on weight constraints and value maximization:

```javascript
// Lines 94-116: DP recurrence
if (currentItemWeight > weightIndex) {
  knapsackMatrix[itemIndex][weightIndex] = knapsackMatrix[itemIndex - 1][weightIndex];
} else {
  knapsackMatrix[itemIndex][weightIndex] = Math.max(
    currentItemValue + knapsackMatrix[itemIndex - 1][weightIndex - currentItemWeight],
    knapsackMatrix[itemIndex - 1][weightIndex],
  );
}

```

After filling the matrix, the algorithm backtracks (lines 118-150) to identify which specific items contributed to the optimal solution.

### Unbounded Knapsack Greedy Logic

The unbounded implementation begins by sorting items to prioritize high-value candidates:

```javascript
// Lines 56-58: Sorting strategy
this.sortPossibleItemsByValue();
this.sortPossibleItemsByValuePerWeightRatio();

```

The greedy fill loop (lines 60-78) calculates how many copies of each item fit within the remaining capacity while respecting stock limits:

```javascript
// Lines 60-78: Greedy item selection
if (this.totalWeight < this.weightLimit) {
  const maxPossibleItemsCount = Math.floor(availableWeight / currentItem.weight);
  // Respect itemsInStock constraint
  currentItem.quantity = Math.min(maxPossibleItemsCount, currentItem.itemsInStock);
  this.selectedItems.push(currentItem);
}

```

## Complexity and Optimality Comparison

Understanding the trade-offs between these implementations helps select the appropriate algorithm for specific constraints:

- **0/1 Knapsack Time Complexity:** **O(n × W)** where `n` is the number of items and `W` is the weight limit. The nested loops over items and weights create this quadratic relationship relative to the weight capacity.
- **0/1 Knapsack Space Complexity:** **O(n × W)** for storing the DP matrix. This can become prohibitive for large weight limits.
- **Unbounded Knapsack Time Complexity:** **O(n log n)** for sorting plus **O(n)** for the greedy traversal, resulting in **O(n log n)** overall.
- **Unbounded Knapsack Space Complexity:** **O(1)** extra space (excluding the input array), making it significantly more memory-efficient.

**Optimality Guarantees:**

- The **0/1 Knapsack** implementation guarantees an **optimal solution** because dynamic programming exhaustively evaluates all feasible combinations without repetition.
- The **Unbounded Knapsack** implementation provides a **heuristic solution** that works well for the fractional knapsack variant but is not universally optimal for the integer unbounded case, though it performs adequately for the repository's sample data.

## Practical Usage Examples

The following examples demonstrate how to instantiate and execute both algorithms using the repository's `Knapsack` and `KnapsackItem` classes:

```javascript
import Knapsack from './src/algorithms/sets/knapsack-problem/Knapsack';
import KnapsackItem from './src/algorithms/sets/knapsack-problem/KnapsackItem';

// ----- 0/1 Knapsack (optimal DP) -----
const items01 = [
  new KnapsackItem({ value: 1, weight: 1 }),
  new KnapsackItem({ value: 4, weight: 3 }),
  new KnapsackItem({ value: 5, weight: 4 }),
  new KnapsackItem({ value: 7, weight: 5 })
];
const maxWeight01 = 7;
const knapsack01 = new Knapsack(items01, maxWeight01);
knapsack01.solveZeroOneKnapsackProblem();

console.log('0/1 total value:', knapsack01.totalValue); // → 9
console.log('0/1 selected items:', knapsack01.selectedItems.map(i => i.toString()));
// Output example: [ 'v4 w3 x 1', 'v5 w4 x 1' ]

// ----- Unbounded Knapsack (greedy) -----
const itemsUnbounded = [
  new KnapsackItem({ value: 84, weight: 7, itemsInStock: 3 }),
  new KnapsackItem({ value: 5,  weight: 2, itemsInStock: 2 }),
  new KnapsackItem({ value: 12, weight: 3, itemsInStock: 1 }),
  new KnapsackItem({ value: 10, weight: 1, itemsInStock: 6 }),
  new KnapsackItem({ value: 20, weight: 2, itemsInStock: 8 })
];
const maxWeightUnb = 15;
const knapsackUnb = new Knapsack(itemsUnbounded, maxWeightUnb);
knapsackUnb.solveUnboundedKnapsackProblem();

console.log('Unbounded total value:', knapsackUnb.totalValue); // → 150
console.log('Unbounded selected items:', knapsackUnb.selectedItems.map(i => i.toString()));
// Example output: [ 'v10 w1 x 6', 'v20 w2 x 4' ]

```

## Summary

- **0/1 Knapsack** uses **dynamic programming** with a matrix-based approach in `solveZeroOneKnapsackProblem()`, guaranteeing optimal solutions when each item can be selected at most once.
- **Unbounded Knapsack** uses a **greedy algorithm** in `solveUnboundedKnapsackProblem()`, allowing unlimited item copies and respecting stock constraints via `itemsInStock`, but sacrificing optimality guarantees for speed.
- The **0/1 implementation** requires **O(n × W)** time and space, while the **unbounded version** runs in **O(n log n)** time with **O(1)** extra space.
- Both implementations reside in [`src/algorithms/sets/knapsack-problem/Knapsack.js`](https://github.com/trekhleb/javascript-algorithms/blob/main/src/algorithms/sets/knapsack-problem/Knapsack.js) and utilize the `KnapsackItem` class to model item properties including `value`, `weight`, and `quantity`.

## Frequently Asked Questions

### What is the main constraint difference between 0/1 and Unbounded Knapsack?

The **0/1 Knapsack** restricts each item to **at most one selection** (binary choice), while the **Unbounded Knapsack** permits **unlimited copies** of any item. This fundamental difference determines whether dynamic programming or greedy strategies are appropriate, as implemented in the `trekhleb/javascript-algorithms` repository.

### Why does the 0/1 Knapsack use dynamic programming while the Unbounded uses a greedy approach?

The **0/1 constraint** creates overlapping subproblems and optimal substructure that require the **dynamic programming** matrix in `solveZeroOneKnapsackProblem()` to explore all combinations without repetition. The **unbounded variant** allows fractional-like filling through repetition, making a **greedy strategy** viable in `solveUnboundedKnapsackProblem()`, though this only guarantees optimality for the fractional knapsack problem, not necessarily the integer unbounded case.

### How does the Unbounded Knapsack handle item stock limits?

The implementation respects the `itemsInStock` property of `KnapsackItem` by capping the calculated quantity during the greedy fill phase. Specifically, in `solveUnboundedKnapsackProblem()`, the algorithm calculates `maxPossibleItemsCount` based on remaining weight, then sets `currentItem.quantity` to the minimum of that capacity and `currentItem.itemsInStock`, ensuring stock constraints are never exceeded.

### Which implementation should I use for large weight capacities?

For **large weight capacities**, the **Unbounded Knapsack** implementation is preferable because it runs in **O(n log n)** time with **O(1)** extra space, whereas the **0/1 Knapsack** requires **O(n × W)** time and space. If your problem requires the 0/1 constraint but has a large weight limit, consider using a 1D DP optimization or alternative algorithms to reduce the space complexity from the matrix-based approach used in `solveZeroOneKnapsackProblem()`.