Skip to content

Commit 9933877

Browse files
Merge branch 'master' into testing/DequeTests
2 parents 96dd008 + ac58493 commit 9933877

File tree

15 files changed

+1218
-56
lines changed

15 files changed

+1218
-56
lines changed

DIRECTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@
304304
- 📄 [KadaneAlgorithm](src/main/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithm.java)
305305
- 📄 [Knapsack](src/main/java/com/thealgorithms/dynamicprogramming/Knapsack.java)
306306
- 📄 [KnapsackMemoization](src/main/java/com/thealgorithms/dynamicprogramming/KnapsackMemoization.java)
307+
- 📄 [KnapsackZeroOne](src/main/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOne.java)
308+
- 📄 [KnapsackZeroOneTabulation](src/main/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTabulation.java)
307309
- 📄 [LevenshteinDistance](src/main/java/com/thealgorithms/dynamicprogramming/LevenshteinDistance.java)
308310
- 📄 [LongestAlternatingSubsequence](src/main/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequence.java)
309311
- 📄 [LongestArithmeticSubsequence](src/main/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequence.java)
@@ -1009,6 +1011,8 @@
10091011
- 📄 [KadaneAlgorithmTest](src/test/java/com/thealgorithms/dynamicprogramming/KadaneAlgorithmTest.java)
10101012
- 📄 [KnapsackMemoizationTest](src/test/java/com/thealgorithms/dynamicprogramming/KnapsackMemoizationTest.java)
10111013
- 📄 [KnapsackTest](src/test/java/com/thealgorithms/dynamicprogramming/KnapsackTest.java)
1014+
- 📄 [KnapsackZeroOneTabulationTest](src/test/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTabulationTest.java)
1015+
- 📄 [KnapsackZeroOneTest](src/test/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTest.java)
10121016
- 📄 [LevenshteinDistanceTests](src/test/java/com/thealgorithms/dynamicprogramming/LevenshteinDistanceTests.java)
10131017
- 📄 [LongestAlternatingSubsequenceTest](src/test/java/com/thealgorithms/dynamicprogramming/LongestAlternatingSubsequenceTest.java)
10141018
- 📄 [LongestArithmeticSubsequenceTest](src/test/java/com/thealgorithms/dynamicprogramming/LongestArithmeticSubsequenceTest.java)

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<dependency>
2121
<groupId>org.junit</groupId>
2222
<artifactId>junit-bom</artifactId>
23-
<version>5.13.3</version>
23+
<version>5.13.4</version>
2424
<type>pom</type>
2525
<scope>import</scope>
2626
</dependency>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* The {@code KnapsackZeroOne} provides Recursive solution for the 0/1 Knapsack
5+
* problem. Solves by exploring all combinations of items using recursion. No
6+
* memoization or dynamic programming optimizations are applied.
7+
*
8+
* Time Complexity: O(2^n) — explores all subsets.
9+
* Space Complexity: O(n) — due to recursive call stack.
10+
*
11+
* Problem Reference: https://en.wikipedia.org/wiki/Knapsack_problem
12+
*/
13+
public final class KnapsackZeroOne {
14+
15+
private KnapsackZeroOne() {
16+
// Prevent instantiation
17+
}
18+
19+
/**
20+
* Solves the 0/1 Knapsack problem using recursion.
21+
*
22+
* @param values the array containing values of the items
23+
* @param weights the array containing weights of the items
24+
* @param capacity the total capacity of the knapsack
25+
* @param n the number of items
26+
* @return the maximum total value achievable within the given weight limit
27+
* @throws IllegalArgumentException if input arrays are null, empty, or
28+
* lengths mismatch
29+
*/
30+
public static int compute(final int[] values, final int[] weights, final int capacity, final int n) {
31+
if (values == null || weights == null) {
32+
throw new IllegalArgumentException("Input arrays cannot be null.");
33+
}
34+
if (values.length != weights.length) {
35+
throw new IllegalArgumentException("Value and weight arrays must be of the same length.");
36+
}
37+
if (capacity < 0 || n < 0) {
38+
throw new IllegalArgumentException("Invalid input: arrays must be non-empty and capacity/n "
39+
+ "non-negative.");
40+
}
41+
if (n == 0 || capacity == 0 || values.length == 0) {
42+
return 0;
43+
}
44+
45+
if (weights[n - 1] <= capacity) {
46+
final int include = values[n - 1] + compute(values, weights, capacity - weights[n - 1], n - 1);
47+
final int exclude = compute(values, weights, capacity, n - 1);
48+
return Math.max(include, exclude);
49+
} else {
50+
return compute(values, weights, capacity, n - 1);
51+
}
52+
}
53+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* Tabulation (Bottom-Up) Solution for 0-1 Knapsack Problem.
5+
* This method uses dynamic programming to build up a solution iteratively,
6+
* filling a 2-D array where each entry dp[i][w] represents the maximum value
7+
* achievable with the first i items and a knapsack capacity of w.
8+
*
9+
* The tabulation approach is efficient because it avoids redundant calculations
10+
* by solving all subproblems in advance and storing their results, ensuring
11+
* each subproblem is solved only once. This is a key technique in dynamic programming,
12+
* making it possible to solve problems that would otherwise be infeasible due to
13+
* exponential time complexity in naive recursive solutions.
14+
*
15+
* Time Complexity: O(n * W), where n is the number of items and W is the knapsack capacity.
16+
* Space Complexity: O(n * W) for the DP table.
17+
*
18+
* For more information, see:
19+
* https://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming
20+
*/
21+
public final class KnapsackZeroOneTabulation {
22+
23+
private KnapsackZeroOneTabulation() {
24+
// Prevent instantiation
25+
}
26+
27+
/**
28+
* Solves the 0-1 Knapsack problem using the bottom-up tabulation technique.
29+
* @param values the values of the items
30+
* @param weights the weights of the items
31+
* @param capacity the total capacity of the knapsack
32+
* @param itemCount the number of items
33+
* @return the maximum value that can be put in the knapsack
34+
* @throws IllegalArgumentException if input arrays are null, of different lengths,or if capacity or itemCount is invalid
35+
*/
36+
public static int compute(final int[] values, final int[] weights, final int capacity, final int itemCount) {
37+
if (values == null || weights == null) {
38+
throw new IllegalArgumentException("Values and weights arrays must not be null.");
39+
}
40+
if (values.length != weights.length) {
41+
throw new IllegalArgumentException("Values and weights arrays must be non-null and of same length.");
42+
}
43+
if (capacity < 0) {
44+
throw new IllegalArgumentException("Capacity must not be negative.");
45+
}
46+
if (itemCount < 0 || itemCount > values.length) {
47+
throw new IllegalArgumentException("Item count must be between 0 and the length of the values array.");
48+
}
49+
50+
final int[][] dp = new int[itemCount + 1][capacity + 1];
51+
52+
for (int i = 1; i <= itemCount; i++) {
53+
final int currentValue = values[i - 1];
54+
final int currentWeight = weights[i - 1];
55+
56+
for (int w = 1; w <= capacity; w++) {
57+
if (currentWeight <= w) {
58+
final int includeItem = currentValue + dp[i - 1][w - currentWeight];
59+
final int excludeItem = dp[i - 1][w];
60+
dp[i][w] = Math.max(includeItem, excludeItem);
61+
} else {
62+
dp[i][w] = dp[i - 1][w];
63+
}
64+
}
65+
}
66+
67+
return dp[itemCount][capacity];
68+
}
69+
}

src/test/java/com/thealgorithms/datastructures/buffers/CircularBufferTest.java

Lines changed: 148 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
44
import static org.junit.jupiter.api.Assertions.assertNull;
5-
import static org.junit.jupiter.api.Assertions.assertThrows;
65
import static org.junit.jupiter.api.Assertions.assertTrue;
76

87
import org.junit.jupiter.api.Test;
@@ -68,11 +67,11 @@ void testFullBuffer() {
6867

6968
@Test
7069
void testIllegalArguments() {
71-
assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(0));
72-
assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(-1));
70+
org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(0));
71+
org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> new CircularBuffer<>(-1));
7372

7473
CircularBuffer<String> buffer = new CircularBuffer<>(1);
75-
assertThrows(IllegalArgumentException.class, () -> buffer.put(null));
74+
org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> buffer.put(null));
7675
}
7776

7877
@Test
@@ -85,4 +84,149 @@ void testLargeBuffer() {
8584
buffer.put(1000); // This should overwrite 0
8685
assertEquals(1, buffer.get());
8786
}
87+
88+
@Test
89+
void testPutAfterGet() {
90+
CircularBuffer<Integer> buffer = new CircularBuffer<>(2);
91+
buffer.put(10);
92+
buffer.put(20);
93+
assertEquals(10, buffer.get());
94+
buffer.put(30);
95+
assertEquals(20, buffer.get());
96+
assertEquals(30, buffer.get());
97+
assertNull(buffer.get());
98+
}
99+
100+
@Test
101+
void testMultipleWrapArounds() {
102+
CircularBuffer<Integer> buffer = new CircularBuffer<>(3);
103+
for (int i = 1; i <= 6; i++) {
104+
buffer.put(i);
105+
buffer.get(); // add and immediately remove
106+
}
107+
assertTrue(buffer.isEmpty());
108+
assertNull(buffer.get());
109+
}
110+
111+
@Test
112+
void testOverwriteMultipleTimes() {
113+
CircularBuffer<String> buffer = new CircularBuffer<>(2);
114+
buffer.put("X");
115+
buffer.put("Y");
116+
buffer.put("Z"); // overwrites "X"
117+
buffer.put("W"); // overwrites "Y"
118+
assertEquals("Z", buffer.get());
119+
assertEquals("W", buffer.get());
120+
assertNull(buffer.get());
121+
}
122+
123+
@Test
124+
void testIsEmptyAndIsFullTransitions() {
125+
CircularBuffer<Integer> buffer = new CircularBuffer<>(2);
126+
assertTrue(buffer.isEmpty());
127+
org.junit.jupiter.api.Assertions.assertFalse(buffer.isFull());
128+
129+
buffer.put(1);
130+
org.junit.jupiter.api.Assertions.assertFalse(buffer.isEmpty());
131+
org.junit.jupiter.api.Assertions.assertFalse(buffer.isFull());
132+
133+
buffer.put(2);
134+
assertTrue(buffer.isFull());
135+
136+
buffer.get();
137+
org.junit.jupiter.api.Assertions.assertFalse(buffer.isFull());
138+
139+
buffer.get();
140+
assertTrue(buffer.isEmpty());
141+
}
142+
143+
@Test
144+
void testInterleavedPutAndGet() {
145+
CircularBuffer<String> buffer = new CircularBuffer<>(3);
146+
buffer.put("A");
147+
buffer.put("B");
148+
assertEquals("A", buffer.get());
149+
buffer.put("C");
150+
assertEquals("B", buffer.get());
151+
assertEquals("C", buffer.get());
152+
assertNull(buffer.get());
153+
}
154+
155+
@Test
156+
void testRepeatedNullInsertionThrows() {
157+
CircularBuffer<Object> buffer = new CircularBuffer<>(5);
158+
for (int i = 0; i < 3; i++) {
159+
int finalI = i;
160+
org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class, () -> buffer.put(null), "Iteration: " + finalI);
161+
}
162+
}
163+
@Test
164+
void testFillThenEmptyThenReuseBuffer() {
165+
CircularBuffer<Integer> buffer = new CircularBuffer<>(3);
166+
167+
buffer.put(1);
168+
buffer.put(2);
169+
buffer.put(3);
170+
assertTrue(buffer.isFull());
171+
172+
assertEquals(1, buffer.get());
173+
assertEquals(2, buffer.get());
174+
assertEquals(3, buffer.get());
175+
176+
assertTrue(buffer.isEmpty());
177+
178+
buffer.put(4);
179+
buffer.put(5);
180+
assertEquals(4, buffer.get());
181+
assertEquals(5, buffer.get());
182+
assertTrue(buffer.isEmpty());
183+
}
184+
185+
@Test
186+
void testPutReturnsTrueOnlyIfPreviouslyEmpty() {
187+
CircularBuffer<String> buffer = new CircularBuffer<>(2);
188+
189+
assertTrue(buffer.put("one")); // was empty
190+
org.junit.jupiter.api.Assertions.assertFalse(buffer.put("two")); // not empty
191+
org.junit.jupiter.api.Assertions.assertFalse(buffer.put("three")); // overwrite
192+
}
193+
194+
@Test
195+
void testOverwriteAndGetAllElementsCorrectly() {
196+
CircularBuffer<Integer> buffer = new CircularBuffer<>(3);
197+
198+
buffer.put(1);
199+
buffer.put(2);
200+
buffer.put(3);
201+
buffer.put(4); // Overwrites 1
202+
buffer.put(5); // Overwrites 2
203+
204+
assertEquals(3, buffer.get());
205+
assertEquals(4, buffer.get());
206+
assertEquals(5, buffer.get());
207+
assertNull(buffer.get());
208+
}
209+
210+
@Test
211+
void testBufferWithOneElementCapacity() {
212+
CircularBuffer<String> buffer = new CircularBuffer<>(1);
213+
214+
assertTrue(buffer.put("first"));
215+
assertEquals("first", buffer.get());
216+
assertNull(buffer.get());
217+
218+
assertTrue(buffer.put("second"));
219+
assertEquals("second", buffer.get());
220+
}
221+
222+
@Test
223+
void testPointerWraparoundWithExactMultipleOfCapacity() {
224+
CircularBuffer<Integer> buffer = new CircularBuffer<>(3);
225+
for (int i = 0; i < 6; i++) {
226+
buffer.put(i);
227+
buffer.get(); // keep buffer size at 0
228+
}
229+
assertTrue(buffer.isEmpty());
230+
assertNull(buffer.get());
231+
}
88232
}

0 commit comments

Comments
 (0)