396. Rotate Function
Problem Description
You have an integer array nums
of length n
. The problem asks you to find the maximum value among all possible rotation functions.
A rotation function F(k)
is calculated for an array that has been rotated k
positions clockwise. When you rotate an array by k
positions clockwise, the last k
elements move to the front of the array.
For each rotation k
, the function F(k)
is computed as:
F(k) = 0 * arr_k[0] + 1 * arr_k[1] + 2 * arr_k[2] + ... + (n-1) * arr_k[n-1]
Where arr_k
is the array after rotating the original nums
by k
positions.
Example to understand rotations:
- If
nums = [4, 3, 2, 6]
arr_0 = [4, 3, 2, 6]
(no rotation)arr_1 = [6, 4, 3, 2]
(rotated 1 position clockwise)arr_2 = [2, 6, 4, 3]
(rotated 2 positions clockwise)arr_3 = [3, 2, 6, 4]
(rotated 3 positions clockwise)
Calculating F(k) for each rotation:
F(0) = 0*4 + 1*3 + 2*2 + 3*6 = 0 + 3 + 4 + 18 = 25
F(1) = 0*6 + 1*4 + 2*3 + 3*2 = 0 + 4 + 6 + 6 = 16
F(2) = 0*2 + 1*6 + 2*4 + 3*3 = 0 + 6 + 8 + 9 = 23
F(3) = 0*3 + 1*2 + 2*6 + 3*4 = 0 + 2 + 12 + 12 = 26
The task is to return the maximum value among all F(k)
where k
ranges from 0
to n-1
. In the example above, the answer would be 26
.
Intuition
The key insight is to find a mathematical relationship between consecutive rotation functions F(k)
and F(k+1)
rather than computing each one from scratch.
Let's observe what happens when we go from F(k)
to F(k+1)
:
- When rotating by one more position, the last element of the current array moves to the front
- Every other element shifts one position to the right, which means their multipliers increase by 1
For example, with nums = [A, B, C, D]
:
F(0) = 0*A + 1*B + 2*C + 3*D
F(1) = 0*D + 1*A + 2*B + 3*C
Notice that when going from F(0)
to F(1)
:
- Elements
A, B, C
each get multiplied by a coefficient that's 1 higher - Element
D
goes from being multiplied by 3 to being multiplied by 0
This means: F(1) = F(0) + A + B + C - 3*D
More generally: F(1) = F(0) + (A + B + C + D) - 4*D
This can be written as: F(1) = F(0) + sum(nums) - n * nums[n-1]
For any transition from F(k)
to F(k+1)
:
F(k+1) = F(k) + sum(nums) - n * nums[n-k-1]
This pattern holds because:
- Adding
sum(nums)
accounts for increasing each element's coefficient by 1 - Subtracting
n * nums[n-k-1]
corrects for the element that wraps around from position(n-1)
to position0
, losing its multiplier of(n-1)
With this recurrence relation, we can compute all F(k)
values in O(n) time by:
- Computing
F(0)
directly - Computing the sum of all elements once
- Using the formula to derive each subsequent
F(k)
from the previous one - Tracking the maximum value encountered
Solution Approach
The implementation follows the mathematical relationship we discovered between consecutive rotation functions:
-
Initialize F(0): First, we calculate
F(0)
directly using the formula:f = sum(i * v for i, v in enumerate(nums))
This computes
0 * nums[0] + 1 * nums[1] + ... + (n-1) * nums[n-1]
-
Precompute constants: We store two values that remain constant throughout:
n, s = len(nums), sum(nums)
n
: the length of the arrays
: the sum of all elements in the array
-
Initialize the answer: Set the initial maximum to
F(0)
:ans = f
-
Iterate through remaining rotations: For each rotation from 1 to n-1, we use the recurrence relation:
for i in range(1, n): f = f + s - n * nums[n - i] ans = max(ans, f)
In each iteration:
f = f + s - n * nums[n - i]
updatesF(i)
based onF(i-1)
- The term
nums[n - i]
identifies the element that was at the last position in the previous rotation and moves to the front s - n * nums[n - i]
represents adding 1 to each element's coefficient (vias
) and then correcting for the element that wraps around (via- n * nums[n - i]
)- We update
ans
with the maximum value seen so far
-
Return the result: After checking all rotations, return the maximum value found:
return ans
Time Complexity: O(n) - We compute F(0) in O(n) time and then compute each subsequent F(k) in O(1) time.
Space Complexity: O(1) - We only use a constant amount of extra space for variables f
, n
, s
, and ans
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [4, 3, 2, 6]
.
Step 1: Calculate F(0)
F(0) = 0*4 + 1*3 + 2*2 + 3*6 = 0 + 3 + 4 + 18 = 25
So f = 25
Step 2: Precompute constants
n = 4 (length of array) s = 4 + 3 + 2 + 6 = 15 (sum of all elements)
Step 3: Initialize answer
ans = 25 (start with F(0))
Step 4: Calculate F(1) using the recurrence relation
When we rotate from position 0 to position 1, the element at index n-1 = 3
(which is 6) moves from the last position to the first position.
Using our formula: F(1) = F(0) + s - n * nums[n-1]
F(1) = 25 + 15 - 4*6 F(1) = 25 + 15 - 24 F(1) = 16
Update: f = 16
, ans = max(25, 16) = 25
Step 5: Calculate F(2)
The element at index n-2 = 2
(which is 2) was at the last position in rotation 1, now moves to the front.
Using our formula: F(2) = F(1) + s - n * nums[n-2]
F(2) = 16 + 15 - 4*2 F(2) = 16 + 15 - 8 F(2) = 23
Update: f = 23
, ans = max(25, 23) = 25
Step 6: Calculate F(3)
The element at index n-3 = 1
(which is 3) was at the last position in rotation 2, now moves to the front.
Using our formula: F(3) = F(2) + s - n * nums[n-3]
F(3) = 23 + 15 - 4*3 F(3) = 23 + 15 - 12 F(3) = 26
Update: f = 26
, ans = max(25, 26) = 26
Step 7: Return the result
return ans = 26
The maximum value among all rotation functions is 26, achieved at rotation k=3.
Solution Implementation
1class Solution:
2 def maxRotateFunction(self, nums: List[int]) -> int:
3 """
4 Find the maximum value of rotation function F(k) for all k from 0 to n-1.
5 F(k) = 0*arr[k] + 1*arr[k+1] + ... + (n-1)*arr[k+n-1]
6 where arr[i] represents the rotated array.
7 """
8 # Calculate initial F(0) = 0*nums[0] + 1*nums[1] + ... + (n-1)*nums[n-1]
9 current_rotation_value = sum(index * value for index, value in enumerate(nums))
10
11 # Get array length and sum of all elements
12 array_length = len(nums)
13 total_sum = sum(nums)
14
15 # Initialize maximum with F(0)
16 max_rotation_value = current_rotation_value
17
18 # Calculate F(1) through F(n-1) using the recurrence relation:
19 # F(k) = F(k-1) + sum(nums) - n * nums[n-k]
20 for rotation in range(1, array_length):
21 # Update current rotation value using the formula
22 # Adding total_sum adds 1 to each element's coefficient
23 # Subtracting n * nums[n-rotation] corrects the last element that wraps to position 0
24 current_rotation_value = current_rotation_value + total_sum - array_length * nums[array_length - rotation]
25
26 # Track the maximum rotation value found so far
27 max_rotation_value = max(max_rotation_value, current_rotation_value)
28
29 return max_rotation_value
30
1class Solution {
2 public int maxRotateFunction(int[] nums) {
3 // Initialize the value of F(0) where F(k) = 0*nums[k] + 1*nums[k+1] + ... + (n-1)*nums[k+n-1]
4 int currentFunctionValue = 0;
5
6 // Sum of all elements in the array
7 int totalSum = 0;
8
9 // Length of the array
10 int arrayLength = nums.length;
11
12 // Calculate F(0) and the total sum of all elements
13 for (int i = 0; i < arrayLength; ++i) {
14 currentFunctionValue += i * nums[i]; // F(0) = 0*nums[0] + 1*nums[1] + ... + (n-1)*nums[n-1]
15 totalSum += nums[i];
16 }
17
18 // Initialize the maximum value with F(0)
19 int maxValue = currentFunctionValue;
20
21 // Calculate F(1), F(2), ..., F(n-1) using the relationship:
22 // F(k) = F(k-1) + sum - n * nums[n-k]
23 // This formula works because when rotating, all elements shift position by 1,
24 // except the last element which moves to position 0
25 for (int rotation = 1; rotation < arrayLength; ++rotation) {
26 currentFunctionValue = currentFunctionValue + totalSum - arrayLength * nums[arrayLength - rotation];
27 maxValue = Math.max(maxValue, currentFunctionValue);
28 }
29
30 return maxValue;
31 }
32}
33
1class Solution {
2public:
3 int maxRotateFunction(vector<int>& nums) {
4 // Calculate initial F(0) and sum of all elements
5 int currentFunctionValue = 0;
6 int sumOfAllElements = 0;
7 int arraySize = nums.size();
8
9 // Initialize F(0) = 0*nums[0] + 1*nums[1] + ... + (n-1)*nums[n-1]
10 // Also calculate the sum of all array elements
11 for (int i = 0; i < arraySize; ++i) {
12 currentFunctionValue += i * nums[i];
13 sumOfAllElements += nums[i];
14 }
15
16 // Track the maximum function value found
17 int maxFunctionValue = currentFunctionValue;
18
19 // Calculate F(k) for k = 1 to n-1 using the recurrence relation:
20 // F(k) = F(k-1) + sum - n * nums[n-k]
21 // This relation comes from the observation that rotating right by 1 position
22 // increases all indices by 1 (mod n), except the last element becomes first
23 for (int rotation = 1; rotation < arraySize; ++rotation) {
24 // Update function value using the recurrence relation
25 currentFunctionValue = currentFunctionValue + sumOfAllElements - arraySize * nums[arraySize - rotation];
26
27 // Update maximum if current value is larger
28 maxFunctionValue = max(maxFunctionValue, currentFunctionValue);
29 }
30
31 return maxFunctionValue;
32 }
33};
34
1/**
2 * Calculates the maximum value of the rotation function F(k) for all possible rotations
3 * F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n-1) * arrk[n-1]
4 * where arrk is the array after rotating k positions clockwise
5 *
6 * @param nums - Input array of numbers
7 * @returns Maximum value among all rotation functions
8 */
9function maxRotateFunction(nums: number[]): number {
10 const arrayLength: number = nums.length;
11
12 // Calculate the sum of all elements in the array
13 const totalSum: number = nums.reduce((accumulator, value) => accumulator + value, 0);
14
15 // Calculate F(0) = 0*nums[0] + 1*nums[1] + ... + (n-1)*nums[n-1]
16 let currentRotationValue: number = nums.reduce((accumulator, value, index) => accumulator + value * index, 0);
17
18 // Initialize the maximum value with F(0)
19 let maxValue: number = currentRotationValue;
20
21 // Calculate F(k) for k from 1 to n-1 using the relationship:
22 // F(k) = F(k-1) + totalSum - n * nums[n-k]
23 // This formula is derived from the pattern when rotating the array
24 for (let rotation = 1; rotation < arrayLength; rotation++) {
25 // Update current rotation value using the mathematical relationship
26 // F(k) = F(k-1) - (totalSum - nums[k-1]) + nums[k-1] * (n-1)
27 // Simplified: F(k) = F(k-1) + totalSum - n * nums[n-k]
28 currentRotationValue = currentRotationValue - (totalSum - nums[rotation - 1]) + nums[rotation - 1] * (arrayLength - 1);
29
30 // Update maximum value if current rotation yields a larger value
31 maxValue = Math.max(maxValue, currentRotationValue);
32 }
33
34 return maxValue;
35}
36
Time and Space Complexity
Time Complexity: O(n)
The algorithm consists of:
- Initial calculation of
f
using list comprehension withenumerate()
:O(n)
- Calculating the sum of all elements
s
:O(n)
- A for loop that runs
n-1
times:O(n)
- Inside the loop, each operation (arithmetic and max comparison) takes
O(1)
- Inside the loop, each operation (arithmetic and max comparison) takes
Since these operations are sequential, the overall time complexity is O(n) + O(n) + O(n) = O(n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- Variable
f
to store the current rotation function value - Variables
n
ands
to store the length and sum - Variable
ans
to track the maximum value - Loop variable
i
All these variables use constant space regardless of the input size, resulting in O(1)
space complexity.
Common Pitfalls
1. Incorrect Index in Recurrence Relation
One of the most common mistakes is using the wrong index when identifying which element moves from the last position to the first during rotation.
Incorrect Implementation:
for i in range(1, n):
f = f + s - n * nums[i] # Wrong! Uses nums[i] instead of nums[n-i]
ans = max(ans, f)
Why it's wrong: When rotating by i
positions, the element that was at the last position in the (i-1)
th rotation and moves to the front is nums[n-i]
, not nums[i]
.
Correct Implementation:
for i in range(1, n):
f = f + s - n * nums[n - i] # Correct index
ans = max(ans, f)
2. Misunderstanding Rotation Direction
Another pitfall is confusing the rotation direction. The problem specifies clockwise rotation, meaning the last elements move to the front.
Example of confusion:
- Original:
[4, 3, 2, 6]
- Rotate 1 clockwise:
[6, 4, 3, 2]
(last element 6 moves to front) - NOT:
[3, 2, 6, 4]
(this would be counter-clockwise)
3. Integer Overflow in Large Arrays
For very large arrays with large values, the rotation function calculations might overflow in languages with fixed integer sizes.
Solution for overflow-prone languages:
- Use appropriate data types (e.g.,
long long
in C++) - In Python, this isn't an issue due to arbitrary precision integers
4. Off-by-One Errors in Loop Range
Forgetting that we need to check exactly n
rotations (from 0 to n-1).
Incorrect:
for i in range(n): # This would try to calculate F(n), which doesn't exist
f = f + s - n * nums[n - i - 1]
Correct:
for i in range(1, n): # Calculates F(1) through F(n-1)
f = f + s - n * nums[n - i]
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
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
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview 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
Want a Structured Path to Master System Design Too? Don’t Miss This!