题目描述
思路解析
一句话答案:LeetCode 396 旋转函数求 F(0)…F(n-1) 的最大值。别对每个旋转都重新加权求和的 O(n²),盯相邻两次旋转的差:F(k)=F(k-1)+sum−n×nums[n-k],一格 O(1) 推出,整体 O(n)。
转一圈里哪次旋转的 F(k) 最大
给一个长度为 n 的数组 nums,顺时针旋转 k 格得到一个新排列,旋转函数 F(k) 就是这个排列的「下标乘元素」加权求和:0×第一个+1×第二个+…+(n-1)×最后一个。题目要 F(0) 到 F(n-1) 里最大的那个。
以 nums=[4,3,2,6] 为例,一路转下去 F(0)=25、F(1)=16、F(2)=23、F(3)=26,最大是 26。要的就是这个 26。
为什么对每个 k 单独加权求和会慢
最顺手的写法是照定义:对每个 k 先把旋转后的数组摆出来,再按下标乘一遍加起来。可算一个 F 就要扫 n 个元素,一共 n 个 F,加起来是 O(n²)(大 O 记号描述规模变大时操作数怎么涨)。
n 上十万,O(n²) 就是百亿级别,跑不动。慢的根子是每次都把整排加权和从零重算,可相邻两次旋转只差一点点,不该当成全新的题。
转一格之后,F 到底变了哪几处
顺时针转一格,原来排在最后的元素跑到最前面,其余每个都往后挪一位。对加权和来说:往后挪的下标全部加 1,跑到队首的那个下标从最大的 n-1 直接掉回 0。
下标集体加 1,等于每个元素都多贡献一份自己,合起来正好多加一个 sum(所有元素之和)。而队首那个元素系数从 n-1 变成 0,等于把它的贡献整块拿掉。递推式就从这两处变化来。
为什么 F(k) 能由 F(k-1) 一步推出
把两处变化拼起来:F(k)=F(k-1)+sum−n×nums[n-k]。加 sum 是下标集体加 1 那份,减的这项是队首元素的贡献变化。递推就是拿前一次算好的值一步推出后一次,不回头重算。
减的为什么是 n 倍那个元素?它系数从 n-1 变 0,本该少 (n-1) 份自己;可它也在「集体加 1」里被 sum 多算了一份,合起来正好减 n 份。转到队首的是 nums[n-k]:顺时针转 k 格,原数组末尾数起第 k 个元素跑到最前,下标恰好 n-k。
这样每推一次只做一次加、一次减,是 O(1)。先算出 F(0) 当起点,再一格格滚到 F(n-1),全程线性。
拿 [4,3,2,6] 把四个 F 逐个滚出来
n=4,sum=4+3+2+6=15。先按定义算起点 F(0)=0×4+1×3+2×2+3×6=0+3+4+18=25,把 25 记成当前最大。
往后套 F(k)=F(k-1)+sum−n×nums[n-k]。F(1)=25+15−4×nums[3]=40−4×6=40−24=16。F(2)=16+15−4×nums[2]=31−4×2=31−8=23。F(3)=23+15−4×nums[1]=38−4×3=38−12=26。四个值 25、16、23、26,最大 26,和题目对上。
单元素和负数这两个边界怎么收
算 F(0) 扫一遍、递推再扫一遍,时间 O(n);只用 f、sum、ans 几个变量,空间 O(1)。
两个边界要想清。一是单元素 nums=[100]:只有 F(0)=0,递推循环根本不进,直接返回 0。二是负数,元素为负照样按同一条公式滚,不用特判;真正要防的是中间 f 值溢出,稳妥起见用 long 累加再返回。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
把这句话记死:「整体加一个 sum,再减掉 n 乘以转到队首的那个元素」。下面每一帧都在套它。
递推需要一个起点,所以第一步先按定义老实算出 F(0),顺便把所有元素之和 sum 也累出来。下面从下标 0 开始,一个元素一个元素地往里加。
看下标 0 的元素 4(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 0 × 4 = 0,把它加进 F(0);同时把 4 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 1 的元素 3(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 1 × 3 = 3,把它加进 F(0);同时把 3 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 2 的元素 2(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 2 × 2 = 4,把它加进 F(0);同时把 2 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 3 的元素 6(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 3 × 6 = 18,把它加进 F(0);同时把 6 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
看下标 4 的元素 4(紫色)。它对 F(0) 的贡献是下标乘元素,也就是 4 × 4 = 16,把它加进 F(0);同时把 4 也累进 sum。左边蓝色是已经算过的,灰色是还没轮到的。
五个元素都加完:F(0) = 41,sum = 19。F(0) 既是第一个候选答案,也是后面递推的起点,所以当前最大 ans 先记成 41。接下来不再重新加权求和,全靠递推一格一格滚。
先看清旋转一格发生了什么:排在最后、系数 4 的红色元素 nums[4]=4 会转到最前面变成系数 0;其余每个元素系数都加 1。系数集体加 1 = 整体加 sum,红色元素从 4× 变 0× = 减掉 n×它。这就是递推式的来历。
第 1 次旋转:这次转到队首的是下标 4 的红色元素 4。套递推式,先把数字代进去:上一帧的 F(0)=41,加上 sum=19,再减掉 n × nums[4] = 5 × 4。
把它算干净:41 + 19 先得 60,再减 5 × 4 = 20,于是 F(1) = 40。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(1)=40 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 2 次旋转:这次转到队首的是下标 3 的红色元素 6。套递推式,先把数字代进去:上一帧的 F(1)=40,加上 sum=19,再减掉 n × nums[3] = 5 × 6。
把它算干净:40 + 19 先得 59,再减 5 × 6 = 30,于是 F(2) = 29。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(2)=29 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 3 次旋转:这次转到队首的是下标 2 的红色元素 2。套递推式,先把数字代进去:上一帧的 F(2)=29,加上 sum=19,再减掉 n × nums[2] = 5 × 2。
把它算干净:29 + 19 先得 48,再减 5 × 2 = 10,于是 F(3) = 38。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(3)=38 和当前最大 41 比:没超过,最大答案保持 41 不动,继续往后滚。
第 4 次旋转:这次转到队首的是下标 1 的红色元素 3。套递推式,先把数字代进去:上一帧的 F(3)=38,加上 sum=19,再减掉 n × nums[1] = 5 × 3。
把它算干净:38 + 19 先得 57,再减 5 × 3 = 15,于是 F(4) = 42。注意这一步只做了一次加法和一次减法,O(1) 就推出来了,完全没有重新加权求和。
拿 F(4)=42 和当前最大 41 比:42 更大,刷新最大答案,ans 变成 42(整排标绿表示出现了新的最大)。
全部 5 个 F 值滚完:F(0) 到 F(4) 分别是 41、40、29、38、42,其中最大的是 F(4) = 42,这就是答案。回头看,我们只在开头老实算了一次 F(0),之后每个 F(k) 都是 O(1) 推出来的,整体 O(n)。
边界先想清:单元素答案恒为 0;长度为 1 时递推循环根本不进;负数也照样按公式滚,不用特判。
认出「相邻状态作差」这个母题,一大类把暴力压成线性的题都能套。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def maxRotateFunction(self, nums: List[int]) -> int: f = sum(i * v for i, v in enumerate(nums)) n, s = len(nums), sum(nums) ans = f for i in range(1, n): f = f + s - n * nums[n - i] ans = max(ans, f) return ans复杂度
- 时间:O(n),算 F(0) 一遍,递推再一遍
- 空间:O(1),只用 f、s、ans 几个变量
易错点
面试追问把动画讲成自己的话
追问怎么想到这条递推的?
追问答案会不会溢出?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
等差数列划分
LeetCode 413 · 中等 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题