Skip to content

Commit 476fc41

Browse files
Dutch National Flag Algorithm - Leetcode 75
1 parent 1803089 commit 476fc41

File tree

3 files changed

+274
-0
lines changed

3 files changed

+274
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package array.dutch_national_flag_algorithm;
2+
3+
/**
4+
* Implements the solution for the LeetCode problem "Sort Colors".
5+
*
6+
* <p>This class uses the Dutch National Flag algorithm to sort an array containing
7+
* only the integers 0, 1, and 2.
8+
*
9+
* @see <a href="https://leetcode.com/problems/sort-colors/">75. Sort Colors</a>
10+
*/
11+
public class SortColors {
12+
13+
/**
14+
* Sorts an array containing 0s, 1s, and 2s in-place using the
15+
* Dutch National Flag algorithm.
16+
*
17+
* <p>The array is partitioned into three sections in a single pass:
18+
* <ul>
19+
* <li>Elements equal to 0 (red) are moved to the beginning.</li>
20+
* <li>Elements equal to 1 (white) remain in the middle.</li>
21+
* <li>Elements equal to 2 (blue) are moved to the end.</li>
22+
* </ul>
23+
* This is achieved with O(N) time complexity and O(1) space complexity.
24+
*
25+
* @param nums The array of integers (containing only 0, 1, or 2) to be sorted.
26+
*/
27+
public void sortColors(int[] nums) {
28+
int low = 0;
29+
int mid = 0;
30+
int high = nums.length - 1;
31+
32+
// The loop must continue as long as the middle pointer has not passed the high pointer.
33+
while (mid <= high) {
34+
switch (nums[mid]) {
35+
case 0:
36+
// If the element is 0, swap it with the element at the low pointer.
37+
swap(nums, low, mid);
38+
low++;
39+
mid++;
40+
break;
41+
case 1:
42+
// If the element is 1, it's in the correct place. Move to the next element.
43+
mid++;
44+
break;
45+
case 2:
46+
// If the element is 2, swap it with the element at the high pointer.
47+
swap(nums, mid, high);
48+
// Decrement high, but do not increment mid, because the new element at mid-
49+
// needs to be processed.
50+
high--;
51+
break;
52+
default:
53+
throw new IllegalStateException("Element not expected");
54+
}
55+
}
56+
}
57+
58+
/**
59+
* Helper method to swap two elements in an array.
60+
*
61+
* @param nums The array in which to swap elements.
62+
* @param i The index of the first element.
63+
* @param j The index of the second element.
64+
*/
65+
private void swap(int[] nums, int i, int j) {
66+
int temp = nums[i];
67+
nums[i] = nums[j];
68+
nums[j] = temp;
69+
}
70+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Provides an implementation of the Dutch National Flag algorithm.
3+
*
4+
* <h2>Dutch National Flag Algorithm</h2>
5+
*
6+
* <p>The Dutch National Flag algorithm, also known as 3-way partitioning, is an
7+
* efficient, in-place sorting algorithm for an array containing three distinct
8+
* values. It was designed by Edsger W. Dijkstra. The algorithm's goal is to
9+
* rearrange the elements into three groups based on their value.
10+
*
11+
* <p>The core idea is to maintain four sections within the array using three
12+
* pointers: {@code low}, {@code mid}, and {@code high}:
13+
* <ol>
14+
* <li><b>Region 1 (0 to low-1):</b> Contains all elements with value 0.</li>
15+
* <li><b>Region 2 (low to mid-1):</b> Contains all elements with value 1.</li>
16+
* <li><b>Region 3 (mid to high):</b> The unprocessed, unknown region.</li>
17+
* <li><b>Region 4 (high+1 to n-1):</b> Contains all elements with value 2.</li>
18+
* </ol>
19+
* The algorithm iterates with the {@code mid} pointer as long as it is less than
20+
* or equal to the {@code high} pointer, processing the element at {@code arr[mid]}:
21+
* <ul>
22+
* <li>If {@code arr[mid]} is 0, it's swapped with the element at {@code arr[low]}.
23+
* Both {@code low} and {@code mid} pointers are then incremented.</li>
24+
* <li>If {@code arr[mid]} is 1, it's in the correct place, so only the {@code mid}
25+
* pointer is incremented.</li>
26+
* <li>If {@code arr[mid]} is 2, it's swapped with the element at {@code arr[high]}.
27+
* The {@code high} pointer is then decremented. The {@code mid} pointer is
28+
* not incremented because the new element at {@code mid} needs to be processed.</li>
29+
* </ul>
30+
* This process continues until the {@code mid} pointer crosses the {@code high} pointer,
31+
* resulting in a sorted array with O(N) time complexity and O(1) space complexity.
32+
*
33+
* <h3>LeetCode Problem: Sort Colors</h3>
34+
*
35+
* <p>A classic application of this algorithm is the "Sort Colors" problem on LeetCode.
36+
* The problem statement is as follows:
37+
* <blockquote>
38+
* Given an array {@code nums} with n objects colored red, white, or blue, sort them
39+
* in-place so that objects of the same color are adjacent, with the colors in the
40+
* order red, white, and blue. We will use the integers 0, 1, and 2 to represent
41+
* the color red, white, and blue, respectively. You must solve this problem
42+
* without using the library's sort function.
43+
* </blockquote>
44+
* This problem maps directly to the Dutch National Flag algorithm, where:
45+
* <ul>
46+
* <li>Red = 0</li>
47+
* <li>White = 1</li>
48+
* <li>Blue = 2</li>
49+
* </ul>
50+
* For more details, see the problem on LeetCode:
51+
* <a href="https://leetcode.com/problems/sort-colors/">75. Sort Colors</a>
52+
*
53+
* @see <a href="https://en.wikipedia.org/wiki/Dutch_national_flag_problem">Dutch national flag problem on Wikipedia</a>
54+
*/
55+
package array.dutch_national_flag_algorithm;
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package array.dutch_national_flag_algorithm;
2+
3+
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
8+
import static org.junit.jupiter.api.Assertions.assertThrows;
9+
10+
/**
11+
* Test suite for the SortColors class.
12+
*/
13+
class SortColorsTest {
14+
15+
private SortColors sorter;
16+
17+
@BeforeEach
18+
void setUp() {
19+
sorter = new SortColors();
20+
}
21+
22+
@Test
23+
@DisplayName("Test with a typical mixed array of 0s, 1s, and 2s")
24+
void testSortColors_withMixedValues() {
25+
int[] nums = {2, 0, 2, 1, 1, 0};
26+
int[] expected = {0, 0, 1, 1, 2, 2};
27+
sorter.sortColors(nums);
28+
assertArrayEquals(expected, nums, "Should sort a mixed array correctly");
29+
}
30+
31+
@Test
32+
@DisplayName("Test with an empty array")
33+
void testSortColors_withEmptyArray() {
34+
int[] nums = {};
35+
int[] expected = {};
36+
sorter.sortColors(nums);
37+
assertArrayEquals(expected, nums, "Should handle an empty array without error");
38+
}
39+
40+
@Test
41+
@DisplayName("Test with a single-element array")
42+
void testSortColors_withSingleElementArray() {
43+
int[] nums = {1};
44+
int[] expected = {1};
45+
sorter.sortColors(nums);
46+
assertArrayEquals(expected, nums, "Should handle a single-element array");
47+
}
48+
49+
@Test
50+
@DisplayName("Test with an already sorted array")
51+
void testSortColors_withAlreadySortedArray() {
52+
int[] nums = {0, 0, 1, 1, 2, 2};
53+
int[] expected = {0, 0, 1, 1, 2, 2};
54+
sorter.sortColors(nums);
55+
assertArrayEquals(expected, nums, "Should not change an already sorted array");
56+
}
57+
58+
@Test
59+
@DisplayName("Test with a reverse-sorted array")
60+
void testSortColors_withReverseSortedArray() {
61+
int[] nums = {2, 1, 0};
62+
int[] expected = {0, 1, 2};
63+
sorter.sortColors(nums);
64+
assertArrayEquals(expected, nums, "Should correctly sort a reverse-sorted array");
65+
}
66+
67+
@Test
68+
@DisplayName("Test with an array containing all the same elements (all 1s)")
69+
void testSortColors_withAllSameElements() {
70+
int[] nums = {1, 1, 1, 1};
71+
int[] expected = {1, 1, 1, 1};
72+
sorter.sortColors(nums);
73+
assertArrayEquals(expected, nums, "Should handle an array of all same elements");
74+
}
75+
76+
@Test
77+
@DisplayName("Test with an array containing all 0s")
78+
void testSortColors_withAllZeros() {
79+
int[] nums = {0, 0, 0};
80+
int[] expected = {0, 0, 0};
81+
sorter.sortColors(nums);
82+
assertArrayEquals(expected, nums, "Should handle an array of all zeros");
83+
}
84+
85+
@Test
86+
@DisplayName("Test with an array containing all 2s")
87+
void testSortColors_withAllTwos() {
88+
int[] nums = {2, 2, 2, 2};
89+
int[] expected = {2, 2, 2, 2};
90+
sorter.sortColors(nums);
91+
assertArrayEquals(expected, nums, "Should handle an array of all twos");
92+
}
93+
94+
@Test
95+
@DisplayName("Test with an array containing only 0s and 1s")
96+
void testSortColors_withOnlyZerosAndOnes() {
97+
int[] nums = {1, 0, 1, 0, 1};
98+
int[] expected = {0, 0, 1, 1, 1};
99+
sorter.sortColors(nums);
100+
assertArrayEquals(expected, nums, "Should correctly sort an array with only 0s and 1s");
101+
}
102+
103+
@Test
104+
@DisplayName("Test with an array containing only 1s and 2s")
105+
void testSortColors_withOnlyOnesAndTwos() {
106+
int[] nums = {1, 2, 1, 2, 2};
107+
int[] expected = {1, 1, 2, 2, 2};
108+
sorter.sortColors(nums);
109+
assertArrayEquals(expected, nums, "Should correctly sort an array with only 1s and 2s");
110+
}
111+
112+
@Test
113+
@DisplayName("Test with an array containing only 0s and 2s")
114+
void testSortColors_withOnlyZerosAndTwos() {
115+
int[] nums = {2, 0, 2, 0};
116+
int[] expected = {0, 0, 2, 2};
117+
sorter.sortColors(nums);
118+
assertArrayEquals(expected, nums, "Should correctly sort an array with only 0s and 2s");
119+
}
120+
121+
@Test
122+
@DisplayName("Test with a complex case where a 2 is swapped with a 0")
123+
void testSortColors_complexSwapCase() {
124+
// This case tests the `high--` without `mid++` logic.
125+
// When nums[mid] (2) is swapped with nums[high] (0), the new nums[mid] is 0,
126+
// which must be processed in the next iteration.
127+
int[] nums = {2, 1, 0};
128+
int[] expected = {0, 1, 2};
129+
sorter.sortColors(nums);
130+
assertArrayEquals(expected, nums, "Should handle swapping a 2 with a 0 correctly");
131+
}
132+
133+
@Test
134+
@DisplayName("Test with a null array should throw NullPointerException")
135+
void testSortColors_withNullArray() {
136+
assertThrows(NullPointerException.class, () -> {
137+
sorter.sortColors(null);
138+
}, "Should throw NullPointerException for a null input array");
139+
}
140+
141+
@Test
142+
@DisplayName("Test with an array having other elements should throw IllegalStateException")
143+
void testSortColors_withUnexpectedArray() {
144+
int[] nums = {5, 1, 0};
145+
assertThrows(IllegalStateException.class, () -> {
146+
sorter.sortColors(nums);
147+
}, "Should throw IllegalStateException for an unexpected input array");
148+
}
149+
}

0 commit comments

Comments
 (0)