package com.freetymekiyan.algorithms.level.medium;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent,
* with the colors in the order red, white and blue.
* <p>
* Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
* <p>
* Note:
* You are not suppose to use the library's sort function for this problem.
* <p>
* Follow up:
* A rather straight forward solution is a two-pass algorithm using counting sort.
* First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then
* 1's and followed by 2's.
* <p>
* Could you come up with an one-pass algorithm using only constant space?
* <p>
* Company Tags: Pocket Gems, Microsoft, Facebook
* Tags: Array, Two Pointers, Sort
* Similar Problems: (M) Sort List, (M) Wiggle Sort, (M) Wiggle Sort II
*/
public class SortColors {
private static final int RED = 0;
private static final int WHITE = 1;
private static final int BLUE = 2;
private SortColors s;
/**
* Two pointers. One-pass.
* Similar to find minimum and second minimum.
* Remember the count of red, and count of red + white.
* Loop through the array.
* For each color, we get its value, and overwrite it with blue.
* Then check if it's red,
*/
public void sortColors(int[] nums) {
int redEnd = -1; // Ending index of red
int whiteEnd = -1; // Ending index of white
for (int i = 0; i < nums.length; i++) {
int v = nums[i];
nums[i] = BLUE;
if (v == RED) {
nums[++whiteEnd] = WHITE; // Update white first
nums[++redEnd] = RED; // If there is no white yet, will overwrite white.
} else if (v == WHITE) {
nums[++whiteEnd] = WHITE;
}
}
}
/**
* 3-way Partitioning. Two pointers. One-pass.
* One pointer redEnd for the end of red.
* The other blueStart for the start of blue from the end.
* If its blue, swap with the end.
* If its red, swap with the start.
* All whites will remain in the middle.
* <p>
* Implementation:
* redEnd = 0, blueStart = n-1
* For each i from 0 to blueStart:
* | While nums[i] is blue and i < blueStart:
* | Swap i with blueStart.
* | Update blueStart to blueStart - 1.
* | While nums[i] is red and i > redEnd:
* | Swap i with redEnd.
* | Update redEnd to redEnd + 1.
*/
public void sortColorsB(int nums[]) {
int redEnd = 0, blueStart = nums.length - 1;
for (int i = 0; i <= blueStart; i++) {
while (nums[i] == BLUE && i < blueStart) { // Move all BLUEs to the end.
swap(nums, i, blueStart--);
}
while (nums[i] == RED && i > redEnd) { // Move all REDs to the front.
swap(nums, i, redEnd++);
}
}
}
/**
* 3-way partitioning. Standard.
* Move red to the front, blue to the end. Keep white in the middle.
* One pointer to the end of red, redEnd, starting from 0.
* Another pointer to the start of blue, blueStart, starting from n-1, go backwards.
* For i = 0 to blueStart:
* | If nums[i] is RED:
* | Swap nums[i] with nums[redEnd].
* | Move redEnd.
* | Can move i too because the color swapped to i won't be red or blue.
* | If nums[i] is BLUE:
* | Swap nums[i] with nums[blueStart].
* | Move blueStart.
* | No need to move i since the color swapped to i can be blue or red.
* | If nums[i] is WHITE:
* | i++, just skip.
*/
public void sortColorsC(int[] nums) {
int redEnd = 0;
int blueStart = nums.length - 1;
int i = 0;
while (i <= blueStart) { // i stops at blueStart, no blueStart - 1.
if (nums[i] == RED) {
swap(nums, i++, redEnd++);
} else if (nums[i] == BLUE) {
swap(nums, i, blueStart--);
} else {
i++;
}
}
}
private void swap(int[] A, int i1, int i2) {
int temp = A[i1];
A[i1] = A[i2];
A[i2] = temp;
}
/**
* Counting sort. Two-pass.
* First iterate through the array to find each color's count.
* Then iterate again and write colors to array.
*/
public void sortColorsD(int[] nums) {
int red = 0;
int white = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == RED) {
red++;
} else if (nums[i] == WHITE) {
white++;
}
}
for (int i = 0; i < nums.length; i++) {
if (i < red) {
nums[i] = RED;
} else if (i < red + white) {
nums[i] = WHITE;
} else {
nums[i] = BLUE;
}
}
}
@Before
public void setUp() {
s = new SortColors();
}
@Test
public void testExamples() {
// Normal case
int[] A = {0, 1, 0, 1, 2, 1, 0};
s.sortColors(A);
Assert.assertArrayEquals(new int[]{0, 0, 0, 1, 1, 1, 2}, A);
// Other test cases
A = new int[]{1, 2, 0};
s.sortColors(A);
Assert.assertArrayEquals(new int[]{0, 1, 2}, A);
A = new int[]{2};
s.sortColors(A);
Assert.assertArrayEquals(new int[]{2}, A);
A = new int[]{2, 2};
s.sortColors(A);
Assert.assertArrayEquals(new int[]{2, 2}, A);
}
@After
public void tearDown() {
s = null;
}
}