LeetCode 189 轮转数组

1. 题目

189. 轮转数组 - 力扣(LeetCode)

题目描述

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

要求尽可能想出多种实现方案,至少满足空间 O (1) 原地解法。

示例

输入:nums = 1,2,3,4,5,6,7, k = 3

输出:5,6,7,1,2,3,4

解释:

向右轮转 1 步: 7,1,2,3,4,5,6

向右轮转 2 步: 6,7,1,2,3,4,5

向右轮转 3 步: 5,6,7,1,2,3,4

输入:nums = -1,-100,3,99, k = 2

输出:3,99,-1,-100

约束

  • (1 <= nums.length <= 10^5)

  • (-2^{31} <= numsi <= 2^{31}-1)

  • (0 <= k <= 10^9)

2. 最佳解题思路描述(三次反转法,原地最优)

核心原理

数组右移 k 位等价于:把后 k 个元素挪到数组前面。利用反转分段实现,三步操作:

  1. 对整个数组全部反转;

  2. 反转前 k 个元素;

  3. 反转剩下 n-k 个元素。

前置关键:

k %= n 取模压缩,消除 k ≥ 数组长度的无效旋转(旋转 n 次等于原数组不变)。

举例 nums=1,2,3,4,5,6,7,k=3,n=7

  1. 整体反转:7,6,5,4,3,2,1

  2. 反转前 3 个:5,6,7,4,3,2,1

  3. 反转后 4 个:5,6,7,1,2,3,4

优势

  • 时间复杂度 (O(n)),每个元素仅被反转 2 次;

  • 空间复杂度 (O(1)),完全原地修改,不新开数组;

  • 代码极简,标准面试最优解。

3. 我的可优化代码(无逻辑错误,标准最优,仅补充说明细节)

复制代码
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k %= n;
        reverse(nums.begin(), nums.end());
        reverse(nums.begin(), nums.begin() + k);
        reverse(nums.begin() + k, nums.end());
    }
};

代码说明

  1. 正确性:逻辑无任何 bug,可通过全部测试用例,是官方推荐最优写法;

  2. 关键点不能省略:

    • k %= n:k 极大时避免无效循环,例如 n=3,k=1000,实际只需右移 1 次;

    • 三次 reverse 顺序固定,调换顺序结果错误;

  3. 小拓展:如果题目要求向左轮转 k 位,可调整反转分段逻辑。

4. 标准规范完整代码(带上头文件说明)

复制代码
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;
        // 整体反转
        reverse(nums.begin(), nums.end());
        // 反转前k段
        reverse(nums.begin(), nums.begin() + k);
        // 反转剩余后段
        reverse(nums.begin() + k, nums.end());
    }
};

5. 总结

  1. 三次反转是本题空间 O (1) 最优模板,代码简洁、效率高,面试首选;

  2. 固定三步口诀:全反转 → 前 k 反转 → 后段反转;

  3. 必须先对 k 取模,否则大数 k 无意义;

  4. 原地类旋转数组优先考虑分段反转思想。

6. 相关知识拓展

拓展 1:额外数组辅助法(空间 O (n),思路简单但不推荐面试)

新建数组存储后 k 位 + 前 n-k 位,再拷贝回原数组:

复制代码
void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    k %= n;
    vector<int> tmp;
    for(int i = n - k; i < n; i++) tmp.push_back(nums[i]);
    for(int i = 0; i < n - k; i++) tmp.push_back(nums[i]);
    nums.swap(tmp);
}

缺点:占用额外数组空间,不符合原地要求。

拓展 2:循环交换法(原地 O (1),逻辑复杂)

从下标 0 开始,每次把元素放到目标旋转位置,记录替换次数,直到全部元素归位,无需反转,但需要额外变量记录中间值,代码可读性差。

拓展 3:反转模板通用场景

数组分段交换、字符串旋转、字符串左移 / 右移均可使用分段反转思想;

例如字符串左旋:abcdefg 左旋 2 位得到 cdefgab,同样用三段反转实现。

拓展 4:复杂度对比

  1. 三次反转原地法:时间 (O(n)),空间 (O(1))(最优);

  2. 辅助数组拷贝:时间 (O(n)),空间 (O(n));

  3. 循环逐个移动暴力法:时间 (O(nk)),大数据超时,完全不推荐。

相关推荐
不会就选b10 小时前
算法日常・每日刷题--<贪心>15
算法
鹿角片ljp10 小时前
Prompt Cache、Token 成本与 Plan Compiler 的工程设计
java·python·算法
萧西待水16 小时前
奥赛一本通 1447 靶形数独
算法·深度优先
泯泷18 小时前
那段文字是谁删的?Yjs 14 正式版之前,一套删除归属方案的实现与边界
前端·javascript·算法
hold?fish:palm18 小时前
34 合并K个升序链表
javascript·算法·链表
Navigator_Z21 小时前
LeetCode //C - 1252. Cells with Odd Values in a Matrix
c语言·算法·leetcode
语戚1 天前
力扣 1621. 大小为K的不重叠线段的数目:动态规划(Java 实现)
java·算法·leetcode·动态规划·力扣·dp
青山木1 天前
Hot 100 --- 最长有效括号
java·数据结构·算法·leetcode·动态规划
这料鬼有毒1 天前
二刷hot100-1143.最长公共子序列
算法·leetcode·动态规划
政企项目老覃1 天前
边缘 AI 推理部署:安防零售场景下的模型裁剪与端侧落地实践
人工智能·程序人生·算法·性能优化·vllm