Skip to content

Three Way Quicksort

Quicksort variant that partitions into less than, equal to, and greater than pivot.

Three way quicksort extends quicksort by handling duplicate keys efficiently. Instead of partitioning into two parts, it splits the array into three regions:

  • elements less than the pivot
  • elements equal to the pivot
  • elements greater than the pivot

This reduces unnecessary comparisons when many elements are equal.

Problem

Given an array AA of length nn, reorder it such that:

A[0]A[1]A[n1] A[0] \le A[1] \le \dots \le A[n-1]

Idea

Use a three way partition (also called Dutch National Flag partition). Maintain three regions:

[<p;;=p;;>p] [ < p ;|; = p ;|; > p ]

During partitioning, elements are rearranged into these regions in one pass.

Algorithm

three_way_quicksort(A, lo, hi):
    if lo >= hi:
        return

    lt = lo
    i = lo
    gt = hi
    pivot = A[lo]

    while i <= gt:
        if A[i] < pivot:
            swap A[lt], A[i]
            lt += 1
            i += 1
        else if A[i] > pivot:
            swap A[i], A[gt]
            gt -= 1
        else:
            i += 1

    three_way_quicksort(A, lo, lt - 1)
    three_way_quicksort(A, gt + 1, hi)

Example

Let:

A=[3,5,3,2,3,1,5] A = [3, 5, 3, 2, 3, 1, 5]

Choose pivot 33.

Partition result:

regionvalues
< 3[2, 1]
= 3[3, 3, 3]
> 3[5, 5]

Recursively sort left and right:

Final result:

[1,2,3,3,3,5,5] [1, 2, 3, 3, 3, 5, 5]

Correctness

The partition step ensures:

  • all elements in the left region are less than the pivot
  • all elements in the middle region equal the pivot
  • all elements in the right region are greater than the pivot

The pivot region is already in final position. Recursively sorting the left and right regions produces a fully sorted array.

Complexity

casetime
bestO(n)O(n) (all equal)
averageO(nlogn)O(n \log n)
worstO(n2)O(n^2)

Three way partition improves performance when duplicates are frequent, often reducing recursion depth.

Space:

casespace
averageO(logn)O(\log n)
worstO(n)O(n)

Properties

propertyvalue
stableno
in-placeyes
duplicate handlingexcellent
practical performancevery strong

Notes

Three way quicksort is especially effective when the input contains many repeated values. Standard quicksort degrades in such cases because it repeatedly partitions equal elements.

This variant avoids that by grouping equal elements in a single step.

Implementation

def three_way_quicksort(a):
    def sort(lo, hi):
        if lo >= hi:
            return

        lt = lo
        i = lo
        gt = hi
        pivot = a[lo]

        while i <= gt:
            if a[i] < pivot:
                a[lt], a[i] = a[i], a[lt]
                lt += 1
                i += 1
            elif a[i] > pivot:
                a[i], a[gt] = a[gt], a[i]
                gt -= 1
            else:
                i += 1

        sort(lo, lt - 1)
        sort(gt + 1, hi)

    sort(0, len(a) - 1)
    return a
func ThreeWayQuickSort(a []int) {
	var sort func(int, int)

	sort = func(lo, hi int) {
		if lo >= hi {
			return
		}

		lt, i, gt := lo, lo, hi
		pivot := a[lo]

		for i <= gt {
			if a[i] < pivot {
				a[lt], a[i] = a[i], a[lt]
				lt++
				i++
			} else if a[i] > pivot {
				a[i], a[gt] = a[gt], a[i]
				gt--
			} else {
				i++
			}
		}

		sort(lo, lt-1)
		sort(gt+1, hi)
	}

	sort(0, len(a)-1)
}