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

相关推荐
Tisfy29 分钟前
LeetCode 0301.删除无效的括号:二进制枚举 / 回溯
算法·leetcode·字符串·题解·回溯·括号匹配·二进制枚举
Nil20837 分钟前
leetcode 322零钱兑换
算法·leetcode·职场和发展
海绵宝宝转agent42 分钟前
2026-10-9 leetcode100刷题+面经整理
笔记·算法·面试
yi0111 小时前
DAY24: LeetCode 167:两数之和 II|从暴力枚举到左右双指针
笔记·python·算法·leetcode·二分查找·双指针
木井巳1 小时前
【BFS 解决拓扑排序】课程表
java·算法·leetcode·广度优先·宽度优先·推荐算法·图搜索算法
扶风ff1 小时前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
yi0111 小时前
LeetCode 15:三数之和|排序 + 双指针,如何避免重复答案?
笔记·python·算法·leetcode·双指针
-dzk-2 小时前
【多维动态规划】LC 1143.最长公共子序列
算法·动态规划
C++ 老炮儿的技术栈2 小时前
char (*csConnectName)[256]; 和 char csConnectName [256] 有什么区别
java·c语言·开发语言·c++·人工智能·算法·c
Omics Pro2 小时前
全新可重分析!代谢组质谱专用
数据库·人工智能·算法·机器学习·自然语言处理