1806. Minimum Number of Operations to Reinitialize a Permutation
Problem Description
You are given an even integer n
. Initially, you have a permutation perm
of size n
where each element of perm
follows the rule perm[i] == i
(with 0-based indexing). You will perform a series of operations on this permutation, and after each operation, a new array arr
is created with the following rules:
- If
i
is even, then the value ofarr[i]
becomes the value ofperm[i / 2]
. - If
i
is odd, then the value ofarr[i]
becomes the value ofperm[n / 2 + (i - 1) / 2]
.
After each operation, the arr
becomes the new perm
, and then you perform the same operation again on this new perm
. The goal is to determine the minimum number of operations required to make the perm
array return to its initial state where each element is perm[i] == i
.
Intuition
To solve the problem efficiently, we need to observe the patterns that emerge when performing the operations on the permutation. The intuition comes from the fact that after each operation, some elements of the permutation will return to their original position before others. In particular, the element at index 1 has the potential to move through each position in the permutation since the operation exchanges elements in a certain cyclical pattern.
The solution involves tracking the new position of the element at index 1 after each operation. The cycle's length is determined by the number of steps it takes for index 1 to return to its original position (as other indices will follow a similar cycle pattern due to the same repetition of operations). The cycle length indicates the minimum non-zero number of operations required to return the entire permutation back to its initial state.
Hence, the approach is to simulate the process, tracking the index of the element at index 1 after each operation until it returns to its starting position. We increment a counter each time we perform an operation, and when the element at index 1 is back to position 1, we have the answer, which is the number of operations performed.
The steps for moving the element from index i during each operation, depending on whether i is even or odd, are directly applied in the code as conditional statements, with bitwise operations used to efficiently perform arithmetic calculations.
The bitwise operations used are:
i < n >> 1
: In place of dividingn
by 2 (n / 2
), we use right shift operation which is faster.i <<= 1
: Instead of multiplyingi
by 2 (i * 2
), we use a left shift, which effectively doubles the value ofi
.i = (i - (n >> 1)) << 1 | 1
: This is an optimized way of doing(2 * (i - n / 2)) + 1
, again using bitwise shift and bitwise OR to set the last bit to 1 for odd positioning.
The solution capitalizes on these patterns and efficient bitwise operations to arrive at a quick and optimal solution.
Learn more about Math patterns.
Solution Approach
The code provided uses a simple while loop to simulate the operations on the permutation and identifies the number of operations needed to bring the array back to its initial state.
- The variable
ans
is used to count the number of operations performed. - The variable
i
represents the current index of the element that started at index 1. The element at index 1 is special because, as per the problem statement, every other element's final position depends on this element's path back to index 1.
The while loop continues indefinitely until the element at index 1 returns to its initial position, which triggers the break condition i == 1
.
Inside the while loop:
- We increment
ans
by 1 for every iteration, representing an operation. - We then use a conditional statement to determine the new position of the element initially at index 1 after the operation.
- If the current index
i
is less than half the size of the permutation (n >> 1
), then the element goes to the positioni * 2
(achieved byi <<= 1
), which is an even-indexed position in the permutation. - If the current index
i
is greater or equal to half the size of the permutation, the element goes to the position(2 * (i - n / 2)) + 1
(achieved byi = (i - (n >> 1)) << 1 | 1
), which is an odd-indexed position.
- If the current index
- After each operation, the conditional checks if the element at index 1 has returned to its initial position. If
i == 1
, the loop terminates, and the variableans
is returned, which represents the minimum number of operations needed to reinitialize the permutation.
There are no additional data structures used, and the algorithm is not complex, as it relies on a simple simulation of the defined operation on the permutation. The pattern identified is that of a cycle wherein the element at index 1 eventually returns to its position after a set number of operations, which is the basis for calculating the answer. This simulation does not require modifying the array at each step and instead tracks the position of a single element, which keeps the space complexity to a constant.
This method is optimal because it avoids unnecessary computation by directly tracking the one element that determines the permutation's return to the initial state, taking advantage of the permuted array's specific structure of cycling through positions in a defined pattern.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with an example where n = 4
. The initial permutation perm
is [0, 1, 2, 3]
.
-
After the first operation, according to the rules:
arr[0] = perm[0 / 2] = perm[0] = 0
arr[1] = perm[4 / 2 + (1 - 1) / 2] = perm[2 + 0] = perm[2] = 2
arr[2] = perm[2 / 2] = perm[1] = 1
arr[3] = perm[4 / 2 + (3 - 1) / 2] = perm[2 + 1] = perm[3] = 3
The resulting
arr
after the first operation is[0, 2, 1, 3]
. -
We now set
perm
to the newarr
, and repeat the operation. The element at index 1 is now at index 2. -
Applying the operation a second time:
arr[0] = perm[0 / 2] = perm[0] = 0
arr[1] = perm[4 / 2 + (1 - 1) / 2] = perm[2 + 0] = perm[2] = 1
arr[2] = perm[2 / 2] = perm[1] = 2
arr[3] = perm[4 / 2 + (3 - 1) / 2] = perm[2 + 1] = perm[3] = 3
The resulting
arr
after the second operation is[0, 1, 2, 3]
, which is the initial state.
Therefore, the number of operations required for the permutation to return to its initial state is 2. This process can be generalised to find the cycle length for any even integer n
.
Following the provided solution approach step-by-step:
- We start with
ans = 0
(number of operations performed) andi = 1
(element at index 1 in the initial permutation). - We execute the while loop with the break condition
i == 1
not met sincei
starts at 1. - In the loop, we use bitwise operations to find the next index
i
based on whether it is currently in the first half or the second half of the permutation. - We continue this process and increment
ans
each time until the element initially at index 1 returns to index 1 again. - The loop terminates, and the value of
ans
tells us the minimum number of operations necessary.
Applying bitwise operations makes each calculation within the loop more efficient compared to using standard arithmetic operations.
Solution Implementation
1class Solution:
2 def reinitializePermutation(self, n: int) -> int:
3 # Initialize the number of operations performed to zero.
4 operation_count = 0
5 # Start with the index of the second element in the permutation.
6 index = 1
7
8 # Use a loop to simulate the permutation process until the original order is restored.
9 while True:
10 # Increment the operation count each time a permutation is applied.
11 operation_count += 1
12 # If the current index is in the first half of the array (before n/2),
13 # apply the permutation formula for the first half.
14 if index < (n >> 1):
15 index <<= 1
16 # If the current index is in the second half of the array (from n/2 to n-1),
17 # apply the permutation formula for the second half.
18 else:
19 index = ((index - (n >> 1)) << 1) | 1
20
21 # If the index is back to 1, the array has been reinitialized.
22 # Return the number of operations performed to reach this point.
23 if index == 1:
24 return operation_count
25
1class Solution {
2 public int reinitializePermutation(int n) {
3 int operationsCount = 0; // Initialize a counter to keep track of the number of operations
4 int index = 1; // Start with the element at index 1
5
6 // Loop indefinitely until we break out when the original position is restored
7 while (true) {
8 operationsCount++; // Increment the operation count
9
10 // If the current index is less than n/2,
11 // it corresponds to the first half of the permuted array
12 if (index < n / 2) {
13 index *= 2; // Double the current index
14 } else {
15 // Otherwise, it corresponds to the second half of the permuted array
16 // Perform the calculation of the new index according to the problem's formula
17 index = (index - n / 2) * 2 + 1;
18 }
19
20 // If the element has returned to its original position (index 1),
21 // break out of the loop
22 if (index == 1) {
23 break;
24 }
25 }
26
27 return operationsCount; // Return the number of operations needed
28 }
29}
30
1class Solution {
2public:
3 // Function to determine the minimum number of operations required
4 // to reinitialize a permutation to its original configuration.
5 int reinitializePermutation(int n) {
6 int numOperations = 0; // Initialize the count of operations to 0
7 for (int index = 1; ; ) { // Start with index set to 1
8 ++numOperations; // Increment the operation count
9
10 // Check if the current index is in the first half of the array
11 if (index < (n / 2)) {
12 // If so, double the index
13 index *= 2;
14 } else {
15 // Else, calculate the new index based on the second half's rule
16 index = (index - (n / 2)) * 2 + 1;
17 }
18
19 // If the index is back to 1, we've completed the reinitialization
20 if (index == 1) {
21 return numOperations; // Return the number of operations needed
22 }
23 // Loop continues until index is back to 1
24 }
25 // No need for a return statement here, as the loop will eventually return
26 // the count once the reinitialization condition is met.
27 }
28};
29
1// Function to determine the minimum number of operations required
2// to reinitialize a permutation to its original configuration.
3function reinitializePermutation(n: number): number {
4 let numOperations: number = 0; // Initialize the count of operations to 0
5 for (let index: number = 1; ; ) { // Start with an index set to 1
6 numOperations++; // Increment the operation count
7
8 // Check if the current index is in the first half of the array.
9 if (index < (n / 2)) {
10 // If so, double the index.
11 index *= 2;
12 } else {
13 // Else, calculate the new index based on the second half's rule.
14 index = (index - (n / 2)) * 2 + 1;
15 }
16
17 // If the index is back to 1, we've completed the reinitialization.
18 if (index === 1) {
19 return numOperations; // Return the number of operations needed.
20 }
21 // Loop continues until index is back to 1.
22 }
23 // No need for a return statement outside of the loop, as the loop will
24 // always return the count once the reinitialization condition is met.
25}
26
27// Example usage:
28// const minOperations = reinitializePermutation(4);
29
Time and Space Complexity
The time complexity of the provided code is O(log n)
. This is because each iteration of the while loop either doubles the value of i
(when i < n >> 1
) or performs a sequence of operations that ultimately results in looping back towards 1 (when i >= n >> 1
). The i
value is halved in terms of its position in the original array. Since the value of i
is halved in every step, it requires at most log2(n)
steps to reach back to the starting position of i = 1
.
The space complexity of the code is O(1)
. This is because the algorithm uses a fixed number of variables (ans
, i
, n
) and does not allocate any additional space that scales with the input size n
. Therefore, the amount of memory used remains constant regardless of the size of n
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of the following is a min heap?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!