1. 题目先看一眼,考点其实很集中
1.1 轮转数组的题目长什么样
先看原题表述:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。示例很经典:
text复制输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
也就是说,每个元素向右移动 k 步,尾部元素跑到头部来。如果你对“轮转”这个词还不太敏感,可以理解成把数组看成一根环形带子,整条带子往右拉了三格,超出去的部分从左边补回来。
这题在 LeetCode 上是第 189 题,标注难度是中等,但说实话它考察的知识点并不复杂,核心就是“数组能不能原地操作”“能不能设计出 O(1) 额外空间的算法”。很多人在面试里一上来就开一个临时数组,把元素搬过去,代码也能跑通,但面试官追问一句“如果数组很大、内存有限,你还能优化吗”,一下子就露怯了。所以这题的关键不是“做出来”,而是“做得省”。
1.2 返回倒数第k个节点的题目长什么样
这一题来自剑指 Offer 22(以及面试题 02.02),题目原文是:输入一个链表,输出该链表中倒数第 k 个节点。示例:
text复制给定一个链表: 1 -> 2 -> 3 -> 4 -> 5,k = 2
返回倒数第 2 个节点: 4
注意这里坑很多,比如“节点编号从 1 开始”“k 保证是合法的吗”“如果链表为空怎么办”。不同平台对边界的定义不太一样,LeetCode 上的版本通常保证 k 合法,但面试时要主动和面试官确认边界,这是加分的点,而不是扣分的点。
两道题一数组一链表,表面上没什么关系,但它们的核心方法论其实是同一套:对基础数据结构进行高效遍历和原地改造。把这俩放在一个系列里讲,是因为它们能帮你同时拿下“数组的索引运算”和“链表的指针操作”这两块基本功,后面学滑动窗口、双指针、循环队列都会受益。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 轮转数组:从“暴力”到“原地”的三层递进
2.1 第一层:复制一份新数组,最直观但空间复杂度不达标
很多人第一反应是:开一个新数组,把每个元素放到它应该在的位置上。公式很简单,nums 里的元素 nums[i] 应该到新数组的下标 (i + k) % n 去,n 是数组长度。代码写出来大概是这样:
python复制def rotate(nums, k):
n = len(nums)
new_arr = [0] * n
for i in range(n):
new_arr[(i + k) % n] = nums[i]
for i in range(n):
nums[i] = new_arr[i]
时间复杂度是 O(n),这个没问题。空间复杂度 O(n),问题就出在这。如果数组长度是 100 万,你就得额外开 100 万长度的空间,这在某些嵌入式场景、大数组场景下是不可接受的。
而且面试官一定会问:能不能不申请额外数组,原地完成?如果不能原地完成,为什么不行?这就逼着你往第二层想。
2.2 第二层:三次翻转法,面试首选的标准答案
三次翻转的思路特别巧妙,我第一次看到的时候是有点惊艳的。核心就一句话:
向右轮转 k 位,等价于先翻转整个数组,再翻转前 k 个元素,再翻转后 n-k 个元素。
拿 [1,2,3,4,5,6,7],k=3 举例:
- 翻转整个数组:
[7,6,5,4,3,2,1] - 翻转前 3 个元素:
[5,6,7,4,3,2,1] - 翻转后 4 个元素:
[5,6,7,1,2,3,4]
结果正好是题目要求。为什么能这样?我的理解是:翻转操作把数组的顺序彻底倒过来,原来在后面的元素集体跑到前面,然后再把两个子区间分别翻转一次,让每个子区间内部的
