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)),大数据超时,完全不推荐。

相关推荐
ZhangShao06071 小时前
题解:CF2249B
c++·算法
小玮看世界1 小时前
[Python]线段树与二分法
数据结构·算法
Tisfy3 小时前
LeetCode 3345.最小可整除数位乘积 I:暴力枚举(从n开始尝试)
数学·算法·leetcode·题解·枚举
DolphinDB智臾科技4 小时前
告别 TB 级数据搬运:DolphinDB 滤波算法库,重塑物联网数据价值链
物联网·算法
可编程芯片开发4 小时前
基于双PI控制器结构的六步逆变器供电无刷直流电机调速simulink仿真
算法
Sagittarius_A*5 小时前
后量子密码|通识认知 02|量子威胁核心原理:Shor 算法如何击穿传统公钥密码
算法·信息安全·密码学·量子计算
青山木5 小时前
Hot 100 --- 在排序数组中查找元素的第一个和最后一个位置
java·数据结构·算法·leetcode
其美杰布-富贵-李5 小时前
聚类算法详解:K-means、层次聚类与 DBSCAN
算法·机器学习·kmeans·聚类
碧海银沙音频科技研究院6 小时前
4 BES2710编译环境搭建方法
嵌入式硬件·算法