Skip to content

In Place Merge Sort

Merge sort variant that reduces auxiliary memory by merging sorted ranges inside the original array.

In place merge sort is a merge sort variant that tries to keep the extra memory small. Standard merge sort uses an auxiliary array of size nn during merging. In place merge sort instead merges two adjacent sorted ranges inside the original array.

The main difficulty is the merge step. Splitting is easy, but merging two sorted adjacent ranges without a large buffer is more complex and usually slower.

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]

while using less than O(n)O(n) auxiliary memory.

Idea

The array is divided recursively as in ordinary merge sort. After both halves are sorted, the algorithm merges them by shifting elements inside the same array.

If the next element in the left half is already less than or equal to the next element in the right half, keep it in place. Otherwise, take the smaller right element and insert it before the current left element by shifting the intervening elements one position to the right.

Algorithm

in_place_merge_sort(A, l, r):
    if l >= r:
        return

    mid = (l + r) // 2

    in_place_merge_sort(A, l, mid)
    in_place_merge_sort(A, mid + 1, r)

    in_place_merge(A, l, mid, r)
in_place_merge(A, l, mid, r):
    i = l
    j = mid + 1

    while i <= mid and j <= r:
        if A[i] <= A[j]:
            i += 1
        else:
            value = A[j]
            k = j

            while k > i:
                A[k] = A[k - 1]
                k -= 1

            A[i] = value

            i += 1
            mid += 1
            j += 1

Example

Let:

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

The two sorted halves are:

[2,5][1,4] [2, 5] \quad [1, 4]

Merge in place:

stepactionarray
1compare 2 and 1, insert 1 before 2[1, 2, 5, 4]
2compare 2 and 4, keep 2[1, 2, 5, 4]
3compare 5 and 4, insert 4 before 5[1, 2, 4, 5]

Final array:

[1,2,4,5] [1, 2, 4, 5]

Correctness

The recursive calls sort the left and right halves. During merging, the prefix before index ii is always sorted and contains the smallest elements seen so far. When A[i]A[j]A[i] \le A[j], keeping A[i]A[i] preserves that invariant. When A[j]<A[i]A[j] < A[i], shifting and inserting A[j]A[j] at position ii places the smallest remaining element next. The invariant continues until one side is exhausted, so the whole range becomes sorted.

Complexity

This simple shifting version has the same recursive split structure as merge sort, but the merge step can take quadratic time in the size of the merged range.

metricvalue
best timeO(nlogn)O(n \log n)
worst timeO(n2)O(n^2)
extra spaceO(1)O(1) plus recursion stack
recursion depthO(logn)O(\log n)

Properties

propertyvalue
stableyes
in-placeyes
adaptivepartly
practical speedusually slower than ordinary merge sort

Notes

There are advanced in place merging algorithms with better asymptotic behavior, but they are substantially harder to implement. Many production systems prefer a small buffer or a full auxiliary array because the code is simpler and faster in practice.

This page describes the basic stable in place version. It is useful for understanding the tradeoff between memory and merge cost.

Implementation

def in_place_merge_sort(a):
    def sort(l, r):
        if l >= r:
            return

        mid = (l + r) // 2
        sort(l, mid)
        sort(mid + 1, r)
        merge(l, mid, r)

    def merge(l, mid, r):
        i = l
        j = mid + 1

        while i <= mid and j <= r:
            if a[i] <= a[j]:
                i += 1
            else:
                value = a[j]
                k = j

                while k > i:
                    a[k] = a[k - 1]
                    k -= 1

                a[i] = value

                i += 1
                mid += 1
                j += 1

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

	sort = func(l, r int) {
		if l >= r {
			return
		}

		mid := (l + r) / 2
		sort(l, mid)
		sort(mid+1, r)
		inPlaceMerge(a, l, mid, r)
	}

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

func inPlaceMerge(a []int, l, mid, r int) {
	i := l
	j := mid + 1

	for i <= mid && j <= r {
		if a[i] <= a[j] {
			i++
		} else {
			value := a[j]
			k := j

			for k > i {
				a[k] = a[k-1]
				k--
			}

			a[i] = value

			i++
			mid++
			j++
		}
	}
}