Dutch National Flag / 3 Way partition problem
Recently while going through interview questions, I came across this interesting problem - Called 3-way partitioning or also called as a Dutch National flag problem.
In gist, the problem is given an array containing 3 distinct elements, (Say 0, 1 and 2) sort them in the increasing order. When you think this way, it makes sense that this is called the Dutch National flag problem (🇳🇱).
Example:
Input array
[1, 1, 2, 0, 1, 2, 0, 1, 0, 2, 1]
Output
[0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2]
Algorithm:
-
Begin with three index variables,
mid,lowandhigh -
midandlowboth begin with lowest index, which is0and high index begin with highest index which isarray.size - 1 -
Keep the
lowandhighindex at same position in the beginning and keep moving themidpointer and swap elements as needed -
To recall, we want all
zeroesto appear first, then all1sand then all2sat the other end -
Starting with current
midposition, repeat following steps from 1 to 5 until mid index is less than or equal to high index- If element at
midindex is zero, swap it with element atlowindex and increment bothmidandlowindices - If element at
midindex is 1, do not perform any swap and simple incrementmidindex, This element is situated at the position we want it to be. This is because all zeroes end up at left end, all 2s end up at right end and all 1s remain in the middle - If the element at mid index is 2, swap elements at mid and high indices. Decrement high index since we have already seen this element and swapped it.
- If element at
-
It is important to note that in the step 3, we are not incrementing
midindex. This is because the element which got swapped with the element athighindex might be0and in the next iteration we may have to put it at the left end. -
If we indeed do increment
midon step 3, and swapped element is 0, we will end up with an array with 0 in the middle rather than on left end -
If it important to note that while swapping we could be swapping two array elements with same value. In order to avoid this, put a check in swap routine which swaps only distinct values
-
When
midindex becomes greater thanhighindex, stop the loop. Our array is now sorted in the increasing order of elements in the sequence0,1and2
Implementation
func threeWaySort(with array: [Int]) -> [Int] {
// Return an array itself if its size is less then 2. It means there is nothing left to sort
if array.count < 2 {
return array
}
// Initialize all three indices.
var low = 0, mid = 0, high = array.count - 1
// Use temparray since we do not want to modify the original array
var tempArray = array
// Continue this while loop until mid <= 0 1 2 high while mid <="high" { if temparray[mid]="=" swap(num1: &temparray[low], num2: &temparray[mid]) low="low" + } else &temparray[high], - return temparray func swap( num1: inout int, int) swap only distinct elements num1="=" num2 let temp="num1" usage inputarray="[1," 1, 2, 0, 1] sortedarray="threeWaySort(with:" inputarray) code>=>
To understand what's going on inside an algorithm, we will print array content along with all 3 indices at the beginning of every iteration
Iteration: 0
Low: 0 Mid: 0 High: 10
[1, 1, 2, 0, 1, 2, 0, 1, 0, 2, 1]
Iteration: 1
Low: 0 Mid: 1 High: 10
[1, 1, 2, 0, 1, 2, 0, 1, 0, 2, 1]
Iteration: 2
Low: 0 Mid: 2 High: 10
[1, 1, 2, 0, 1, 2, 0, 1, 0, 2, 1]
Iteration: 3
Low: 0 Mid: 2 High: 9
[1, 1, 1, 0, 1, 2, 0, 1, 0, 2, 2]
Iteration: 4
Low: 0 Mid: 3 High: 9
[1, 1, 1, 0, 1, 2, 0, 1, 0, 2, 2]
Iteration: 5
Low: 1 Mid: 4 High: 9
[0, 1, 1, 1, 1, 2, 0, 1, 0, 2, 2]
Iteration: 6
Low: 1 Mid: 5 High: 9
[0, 1, 1, 1, 1, 2, 0, 1, 0, 2, 2]
Iteration: 7
Low: 1 Mid: 5 High: 8
[0, 1, 1, 1, 1, 2, 0, 1, 0, 2, 2]
Iteration: 8
Low: 1 Mid: 5 High: 7
[0, 1, 1, 1, 1, 0, 0, 1, 2, 2, 2]
Iteration: 9
Low: 2 Mid: 6 High: 7
[0, 0, 1, 1, 1, 1, 0, 1, 2, 2, 2]
Iteration: 10
Low: 3 Mid: 7 High: 7
[0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2]
Now, having implemented the actual code, let's write some unit tests to test the program with few boundary cases.
import XCTest
@testable import AlgorithmsPractice
class AlgorithmsPracticeTests: XCTestCase {
func testDutchFlagProblem() {
let emptyArray: [Int] = []
let sortedArray = [0, 0, 1, 1, 2, 2]
let reverseSortedArray = [2, 2, 1, 1, 0, 0]
let randomArray = [0, 1, 2, 0, 2, 1, 2, 0, 1]
let sameElementArray = [0, 0, 0, 0]
let twoDistinctElementsArray = [0, 1, 1, 0]
XCTAssertEqual(threeWaySort(with: emptyArray), [])
XCTAssertEqual(threeWaySort(with: sortedArray), [0, 0, 1, 1, 2, 2])
XCTAssertEqual(threeWaySort(with: reverseSortedArray), [0, 0, 1, 1, 2, 2])
XCTAssertEqual(threeWaySort(with: randomArray), [0, 0, 0, 1, 1, 1, 2, 2, 2])
XCTAssertEqual(threeWaySort(with: sameElementArray), [0, 0, 0, 0])
XCTAssertEqual(threeWaySort(with: twoDistinctElementsArray), [0, 0, 1, 1])
}
}
Time complexity
As you can see from above algorithm, we maintain 3 indices to traverse over an array, but in the worst case we traverse the entire array just once. So if we assume the size of input array is n, the time complexity in terms of big O notation is simply O(n)
Reference:
Dutch National Flag Algorithm - GeeksForGeeks
Dutch National Flag Algorithm - Wikipedia

