Minimum Adjacent Swaps to Sort an Array
Given an array nums of distinct integers, return the minimum number of adjacent swaps needed to sort it in ascending order. Bubble sort only ever swaps adjacent out-of-order pairs, and each such swap fixes exactly one inversion — so the number of swaps bubble sort performs is already the minimum possible. Simulating it directly is O(n^2), which is too slow here; instead, count inversions in O(n log n) using a merge-sort-style count, which gives the same answer bubble sort would arrive at, without the wasted comparisons.
Constraints
- 1 ≤ nums.length ≤ 105
- -109 ≤ numsi ≤ 109
- All values in nums are distinct
Example
nums = [4, 3, 1, 2]5Explanation There are 5 inversions in [4, 3, 1, 2]: (4,3), (4,1), (4,2), (3,1), (3,2) — each needs exactly one adjacent swap to fix.
In plain terms
- Inversion
- A pair of positions (i, j) with i before j where numsi is bigger than numsj — exactly the kind of pair bubble sort fixes with one swap.
Count inversions with a merge-sort sweep
Split [4, 3, 1, 2] into [4, 3] and [1, 2], and recurse on each half.
What happens in this step
[4, 3, 1, 2] → left [4, 3], right [1, 2] Splitting in half is what makes this O(n log n) instead of O(n^2) — each half's inversions get counted independently before the halves are merged back together.
Steps to visualize
- The row is the whole array, four cells wide; the highlight shows the part being worked on.
- Split the array in half and recursively count inversions in each half.
- Merge the two sorted halves back together.
- Whenever an element from the right half is placed before remaining elements of the left half, every one of those remaining left elements forms an inversion with it — add that count.
- The sum of all these counts, across every merge step, is the minimum number of adjacent swaps to sort the array.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
Split [4, 3, 1, 2] into [4, 3] and [1, 2], and recurse on each half.
What happens in this step
[4, 3, 1, 2] → left [4, 3], right [1, 2] Splitting in half is what makes this O(n log n) instead of O(n^2) — each half's inversions get counted independently before the halves are merged back together.
Solution
function minSwapsToSort(nums) {
function mergeCount(arr, left, right) {
if (right - left <= 1) {
return 0;
}
const mid = Math.floor((left + right) / 2);
let count = mergeCount(arr, left, mid) + mergeCount(arr, mid, right);
const merged = [];
let i = left;
let j = mid;
while (i < mid && j < right) {
if (arr[i] <= arr[j]) {
merged.push(arr[i]);
i++;
} else {
merged.push(arr[j]);
j++;
count += mid - i;
}
}
while (i < mid) merged.push(arr[i++]);
while (j < right) merged.push(arr[j++]);
for (let k = 0; k < merged.length; k++) {
arr[left + k] = merged[k];
}
return count;
}
const copy = nums.slice();
return mergeCount(copy, 0, copy.length);
}- Time
- O(n log n)
- Space
- O(n)
Test cases
| Input | Expected | Covers |
|---|---|---|
nums = [4, 3, 1, 2] | 5 | example from the docstring |
nums = [1, 2, 3, 4, 5] | 0 | already sorted, zero swaps needed |
nums = [5, 4, 3, 2, 1] | 10 | worst case, maximum possible inversions |
nums = [10] | 0 | smallest valid input, a single element |
nums = [2, 1] | 1 | boundary case, exactly one inversion |
nums = [1, 3, 2] | 1 | only one pair out of order |
nums = [3, 1, 2, 5, 4] | 3 | a permutation with inversions spread across non-adjacent positions |