

文章目录
摘要
在日常开发中,我们经常需要处理"旋转"、"偏移"、"滑动"这类序列操作,比如日志轮转、时间序列分析、或者数据窗口偏移等。而这道题------LeetCode 396《旋转函数》就是个典型例子,它看起来是一道数学题,但实质上是考察你对数组规律和前后状态转换的理解。
这题很容易写出暴力解法(每次旋转都重新计算),但那会超时。
要真正写出高效算法,我们得想办法在O(n) 时间内,复用上一次计算结果,这个思路在数据分析和算法优化中都非常有用。

描述
题目给定一个长度为 n 的整数数组 nums,我们定义:
- 
对数组顺时针旋转
k次后得到新数组arr_k; - 
对这个数组计算函数:
F(k) = 0 * arrk[0] + 1 * arrk[1] + ... + (n - 1) * arrk[n - 1] 
我们需要找出所有 F(k) 中的最大值。
示例 1
输入: nums = [4,3,2,6]
输出: 26
解释:
F(0) = 25
F(1) = 16
F(2) = 23
F(3) = 26
最大值是 F(3) = 26
        示例 2
            
            
              txt
              
              
            
          
          输入: nums = [100]
输出: 0
        题解答案
这题最开始很多人会想:
"我把数组每次旋转一位,然后再计算一遍 F(k) 不就行了吗?"
确实可行,但复杂度是 O(n²) ,在 n = 10⁵ 时就炸了。
所以我们需要找到更巧的方式来从 F(k-1) 推导出 F(k)。
关键发现是:
每次旋转后,数组中所有元素的下标都变化了 1。
我们可以通过数学公式,找出
F(k)和F(k-1)之间的关系。

题解代码分析
先放上完整 Swift 解法
            
            
              swift
              
              
            
          
          import Foundation
class Solution {
    func maxRotateFunction(_ nums: [Int]) -> Int {
        let n = nums.count
        if n == 0 { return 0 }
        // 计算数组元素总和
        let sum = nums.reduce(0, +)
        // 初始的 F(0)
        var f0 = 0
        for i in 0..<n {
            f0 += i * nums[i]
        }
        var maxValue = f0
        var fk = f0
        // 利用公式递推计算 F(k)
        // F(k) = F(k-1) + sum - n * nums[n-k]
        for k in 1..<n {
            fk = fk + sum - n * nums[n - k]
            maxValue = max(maxValue, fk)
        }
        return maxValue
    }
}
        代码拆解讲解
我们一步步看清楚算法的推理过程
1. 先算出 F(0)
也就是不旋转的初始值:
            
            
              swift
              
              
            
          
          for i in 0..<n {
    f0 += i * nums[i]
}
        举例:
nums = [4,3,2,6]
F(0) = 0*4 + 1*3 + 2*2 + 3*6 = 25
2. 找规律:F(k) 与 F(k-1) 的关系
当数组旋转一次时,所有元素的权重(下标)都往后移一位。
于是我们有:
F(k) = F(k-1) + sum(nums) - n * nums[n - k]
        解释:
sum(nums)是所有元素的总和;n * nums[n - k]是被"旋转到头"的元素需要被扣掉的部分。
这一步非常关键,它让我们从 O(n²) 降到了 O(n)。
3. 迭代更新 F(k)
我们只要根据上面这个递推式,不断计算新的 F(k),同时取最大值:
            
            
              swift
              
              
            
          
          for k in 1..<n {
    fk = fk + sum - n * nums[n - k]
    maxValue = max(maxValue, fk)
}
        这个循环跑一遍数组就结束了,复杂度只有 O(n)。
示例测试及结果
我们来跑几个例子:
            
            
              swift
              
              
            
          
          let solution = Solution()
print(solution.maxRotateFunction([4,3,2,6]))   // 输出:26
print(solution.maxRotateFunction([100]))       // 输出:0
print(solution.maxRotateFunction([1,2,3,4,5])) // 输出:40
        输出结果:
            
            
              txt
              
              
            
          
          26
0
40
        解释:
[4,3,2,6]→ 最大 F(k) = 26;[100]→ 只有一个数,无论怎么旋转都是 0;[1,2,3,4,5]→ 最大值出现在最后一次旋转时。
时间复杂度
整个算法只遍历数组两次:
- 一次计算总和;
 - 一次计算递推 F(k)。
 
所以时间复杂度为 O(n)。
这比暴力计算的 O(n²) 提速了几个数量级。
空间复杂度
只用到几个变量(sum、f0、fk、maxValue),没有额外数据结构。
所以空间复杂度是 O(1),非常高效。
总结
这道题表面是"数组旋转",实质是一个数学公式优化问题 。
关键在于你能不能发现 F(k) 与 F(k-1) 的线性关系。
一旦抓住了 F(k) = F(k-1) + sum - n * nums[n - k] 这个规律,整个题目立刻变得优雅又高效。
在实际开发中,这种"从前一个状态推下一个状态"的技巧非常常见,比如:
- 时间序列分析中,用上一个窗口结果推下一个;
 - 滑动平均计算中,用上次结果减去旧值加上新值;
 - 性能优化时,减少重复计算。