3942. Minimum Operations to Sort a Permutation
Difficulty: Medium
Topics: Staff, Array, Weekly Contest 503
You are given an integer array nums of length n, where nums is a permutation1 of the integers from 0 to n - 1.
You may perform only the following operations:
- Reverse the entire array.
- Rotate Left by One: Move the first element to the end of the array, and rest elements to left by one position.
Return an integer denoting the minimum number of operations required to sort the array in increasing order. If it is not possible to sort the array using only the given operations, return -1.
Example 1:
- Input: nums = [0,2,1]
- Output: 2
-
Explanation:
- Rotate Left by one:
[2, 1, 0] - Reverse the array:
[0, 1, 2] - The array becomes sorted in 2 operations, which is minimal
- Rotate Left by one:
Example 2:
- Input: nums = [1,0,2]
- Output: 2
-
Explanation:
- Reverse the array:
[2, 0, 1] - Rotate Left by one:
[0, 1, 2] - The array becomes sorted in 2 operations, which is minimal.
- Reverse the array:
Example 3:
- Input: nums = [2,0,1,3]
- Output: -1
-
Explanation: Neither
numsnor its reverse[3,1,0,2]is a cyclic increasing sequence, so it is impossible to reach[0,1,2,3]using the allowed operations. Thus, the answer is -1.
Example 4:
- Input: nums = [0]
- Output: 0
- Explanation: The array has only one element, so it is already sorted. No operations are needed.
Example 5:
- Input: nums = [0,1,2,3]
- Output: 0
- Explanation: The array is already sorted in increasing order. No operations are needed.
Example 6:
- Input: nums = [3,2,1,0]
- Output: 1
-
Explanation:
- Reverse the array:
[0, 1, 2, 3] - The array becomes sorted in 1 operation, which is minimal.
- Reverse the array:
Example 7:
- Input: nums = [1,0]
- Output: 1
-
Explanation:
- Reverse the array:
[0, 1](or Rotate Left by one:[0, 1]) - The array becomes sorted in 1 operation, which is minimal.
- Reverse the array:
Example 8:
- Input: nums = [1,2,0]
- Output: 2
-
Explanation:
- Rotate Left by one:
[2, 0, 1] - Rotate Left by one:
[0, 1, 2] - The array becomes sorted in 2 operations, which is minimal.
- Rotate Left by one:
Example 9:
- Input: nums = [2,1,0]
- Output: 1
-
Explanation:
- Reverse the array:
[0, 1, 2] - The array becomes sorted in 1 operation, which is minimal.
- Reverse the array:
Example 10:
- Input: nums = [1,2,3,0]
- Output: 3
-
Explanation:
- Rotate Left by one:
[2, 3, 0, 1] - Rotate Left by one:
[3, 0, 1, 2] - Rotate Left by one:
[0, 1, 2, 3] - The array becomes sorted in 3 operations, which is minimal. (Reverse-then-rotate-then-reverse also costs 3.)
- Rotate Left by one:
Example 11:
- Input: nums = [0,2,1,3]
- Output: -1
-
Explanation: Neither
numsnor its reverse[3,1,2,0]is a cyclic increasing sequence —numsfails at index 1 (expected 1, got 2). Thus, the sorted array[0,1,2,3]is unreachable, and the answer is -1.
Example 12:
- Input: nums = [2,3,0,1]
- Output: 2
-
Explanation:
- Rotate Left by one:
[3, 0, 1, 2] - Rotate Left by one:
[0, 1, 2, 3] - The array becomes sorted in 2 operations, which is minimal.
- Rotate Left by one:
Constraints:
1 <= n == nums.length <= 10⁵0 <= nums[i] <= n - 1-
numsis a permutation of integers from 0 ton - 1.
Hint:
-
1a. (Case Analysis) The operations generate only rotations of
numsand rotations ofreverse(nums). -
1b. (Case Analysis) So sorting is possible if and only if
numsis a rotation of increasing order, or a rotation of decreasing order. -
1c. (Case Analysis) If nums is increasing cyclically and
nums[i] = 0, then rotating leftitimes sorts the array, or we may reverse, rotate, then reverse again. Take the minimum between these possibilities. -
1d. (Case Analysis) If
numsis decreasing cyclically, try both possibilities: reverse first, or rotate first and then reverse. Take the minimum valid cost. -
2a. (Breadth-First Search) As follows from Hint 1a and 1b, each reachable array can be represented as a state. Since all reachable arrays belong to one of two families, we can compress a state into
(type, shift).type = 0: a rotation of the original array.type = 1: a rotation of the reversed array.shiftdenotes the number of left rotations applied within that family. -
2b. (Breadth-First Search) A left rotation changes
shiftto(shift + 1) % n. A reverse togglestype, while also changing the correspondingshiftto(n - shift) % n. -
2c. (Breadth-First Search) Build a graph on these
2nstates using the two allowed operations, then run BFS from(0, 0)to the state representing the input array.
Solution:
We model the entire reachable state space as exactly 2n compressed states — either a left-rotation of nums (type 0) or a left-rotation of reverse(nums) (type 1) — since reversing twice or rotating n times returns to the origin, and any sequence of the two operations keeps you inside this set. From the initial state (type=0, shift=0), we run a BFS over these 2n states using the two legal transitions, and stop as soon as we hit any state whose underlying array is sorted. This gives the true minimum operation count in O(n) time.
Approach
-
Reduce the state space: Both allowed operations preserve the "shape" of the array up to rotation and reversal, so every reachable array is either
rotateLeft(nums, s)orrotateLeft(reverse(nums), s)for somes ∈ [0, n). -
Encode a state as
(type, shift):-
type = 0→ rotation ofnums -
type = 1→ rotation ofreverse(nums) -
shift→ number of left-rotations applied inside that family
-
-
Define the two transitions:
-
Rotate left:
(type, shift) → (type, (shift + 1) mod n) -
Reverse:
(type, shift) → (1 − type, (n − shift) mod n)— reversing a rotationsofAyields rotation(n − s) mod nofreverse(A)
-
Rotate left:
-
Compute target states: A state is a target iff its underlying array is
[0, 1, …, n−1]. This requires the array to be a cyclic increasing sequence — locate0at positionzeroPosand verifyB_type[(zeroPos + j) % n] == jfor allj. If so,zeroPosleft-rotations sort it. -
BFS from
(0, 0): Since every edge has weight1, BFS returns the shortest sequence of operations to any target state. If BFS exhausts all states without reaching a target, return-1.
Let's implement this solution in PHP: 3942. Minimum Operations to Sort a Permutation
<?php
/**
* @param Integer[] $nums
* @return Integer
*/
function minOperations(array $nums): int
{
...
...
...
/**
* go to ./solution.php
*/
}
/**
* Alias in case the judge expects this method name.
*
* @param array $nums
* @return int
*/
function sortPermutation(array $nums): int {
...
...
...
/**
* go to ./solution.php
*/
}
/**
* Returns the shift needed to make B_t sorted, or -1 if impossible.
* B_0 = nums
* B_1 = reverse(nums)
*
* @param array $nums
* @param int $type
* @return int
*/
function getTargetShift(array $nums, int $type): int {
...
...
...
/**
* go to ./solution.php
*/
}
// Test cases
echo minOperations([0, 2, 1]) . "\n"; // Output: 2
echo minOperations([1, 0, 2]) . "\n"; // Output: 2
echo minOperations([2, 0, 1, 3]) . "\n"; // Output: -1
echo minOperations([0]) . "\n"; // Output: 0
echo minOperations([0, 1, 2, 3]) . "\n"; // Output: 0
echo minOperations([3, 2, 1, 0]) . "\n"; // Output: 1
echo minOperations([1, 0]) . "\n"; // Output: 1
echo minOperations([1, 2, 0]) . "\n"; // Output: 2
echo minOperations([2, 1, 0]) . "\n"; // Output: 1
echo minOperations([1, 2, 3, 0]) . "\n"; // Output: 3
echo minOperations([0, 2, 1, 3]) . "\n"; // Output: -1
echo minOperations([2, 3, 0, 1]) . "\n"; // Output: 2
?>
Explanation:
-
Initialization
- Compute the target
(type, shift)pair(s) usinggetTargetShift()— at most one per type. - If neither family is cyclic-increasing, return
-1immediately. - Build a
distarray of size2n, initializedist[0] = 0(starting state(0, 0)), and push0into a FIFO queue.
- Compute the target
-
BFS loop
- Pop state
id = type * n + shift. - If
idis a target, returndist[id]— this is the minimum number of operations. -
Apply reverse:
newId = (1 − type) * n + ((n − shift) mod n); if unvisited, enqueue withdist[id] + 1. -
Apply rotate-left:
newId = type * n + ((shift + 1) mod n); if unvisited, enqueue withdist[id] + 1.
- Pop state
-
Why BFS, not greedy case analysis?
- The reachable graph is small (
2nnodes, degree 2) and unweighted, so BFS is both simplest and asymptotically optimal. It transparently handles both "rotate-then-reverse" and "reverse-then-rotate" orderings without special-casing.
- The reachable graph is small (
-
Target verification (
getTargetShift)- Extract
zeroPosinB_type. - Walk around the circle checking
B_type[(zeroPos + j) % n] == jforj = 0 … n−1. - If any mismatch, this family can never be sorted → return
-1. - Otherwise, return
zeroPos— the exact number of left rotations needed to bring0to the front.
- Extract
Complexity Analysis
-
Time:
O(n)— computing targets takesO(n), and BFS visits each of the2nstates at most once withO(1)work per state. -
Space:
O(n)—distarray of size2n,queueof size2n, and the smalltargets/isTargetmaps. -
Total:
O(n)time,O(n)space, well within then ≤ 10⁵limit
Contact Links
If you found this series helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:
-
Permutation: A permutation is a rearrangement of all the elements of an array. ↩
Top comments (0)