LeetCode热题100--189

189. 轮转数组

题目链接

题目描述

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

示例 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]

解题思路

关键点

  1. 注意处理 k 可能大于数组长度的情况,需要使用取模运算
  2. 需要原地修改数组,不能使用额外的数组(虽然方法1和方法2用了额外空间,但题目要求O(1)空间)

三种解法对比

方法一:双倍数组法

cpp 复制代码
void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    k = k % n;  // 处理k大于n的情况
    vector<int> nums1(nums);  // 复制原数组
    nums1.insert(nums1.end(), nums.begin(), nums.end());  // 拼接两份
    nums.clear();
    nums.insert(nums.begin(), nums1.begin() + n - k, nums1.begin() + 2 * n - k);
}

思路解析:

  1. 先把原数组复制一份,然后拼接到自己后面
  2. 从新数组的 n-k 位置开始,取 n 个元素
  3. 用这个子数组替换原数组

示例:

复制代码
nums = [1,2,3,4,5], k=2, n=5
nums1 = [1,2,3,4,5,1,2,3,4,5]
从位置 n-k=3 开始取5个:nums1[3]=4, [4]=5, [5]=1, [6]=2, [7]=3
结果:[4,5,1,2,3]

复杂度:

  • 时间复杂度:O(n),需要遍历数组
  • 空间复杂度:O(n),需要额外的数组空间

方法二:额外数组法

cpp 复制代码
void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    vector<int> nums1(n);
    for(int i = 0; i < n; i++) {
        nums1[(i + k) % n] = nums[i];
    }
    nums.assign(nums1.begin(), nums1.end());
}

思路解析:

  1. 创建一个和原数组同样大小的新数组
  2. 遍历原数组,将每个元素放到新数组的 (i+k)%n 位置
  3. 用新数组替换原数组

示例:

复制代码
nums = [1,2,3,4,5], k=2, n=5
i=0: nums1[(0+2)%5=2] = 1 → nums1[2]=1
i=1: nums1[(1+2)%5=3] = 2 → nums1[3]=2
i=2: nums1[(2+2)%5=4] = 3 → nums1[4]=3
i=3: nums1[(3+2)%5=0] = 4 → nums1[0]=4
i=4: nums1[(4+2)%5=1] = 5 → nums1[1]=5
nums1 = [4,5,1,2,3]

复杂度:

  • 时间复杂度:O(n),需要遍历数组
  • 空间复杂度:O(n),需要额外的数组空间

方法三:三次反转法(最优解)

cpp 复制代码
void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    k = k % n;
    reverse(nums.begin(), nums.end());          // 反转整个数组
    reverse(nums.begin(), nums.begin() + k);     // 反转前k个
    reverse(nums.begin() + k, nums.end());      // 反转剩下的
}

思路解析:

  1. 先反转整个数组
  2. 再反转前k个元素
  3. 最后反转剩下的n-k个元素

示例:

复制代码
nums = [1,2,3,4,5], k=2, n=5
1. 整体反转: [5,4,3,2,1]
2. 反转前k=2个: [4,5,3,2,1]
3. 反转剩下的n-k=3个: [4,5,1,2,3]

数学原理:

向右旋转k位,相当于把后k个元素移到前面。通过三次反转可以巧妙地实现这个操作:

  • 整体反转:把末尾元素放到开头
  • 反转前k个:恢复被反转的尾部顺序
  • 反转剩下的:恢复被反转的头部顺序

复杂度:

  • 时间复杂度:O(n),三次反转操作
  • 空间复杂度:O(1),原地修改,不需要额外空间

总结对比

方法 时间复杂度 空间复杂度 优点 缺点
双倍数组法 O(n) O(n) 思路直观,容易理解 需要额外O(n)空间
额外数组法 O(n) O(n) 逻辑简单清晰 需要额外O(n)空间
三次反转法 O(n) O(1) 最优解,原地修改 需要理解反转原理

注意事项

  1. 必须处理 k > n 的情况,通过 k = k % n 取模
  2. 题目要求原地修改,但有些方法用了额外空间
  3. 三种方法都要掌握,但面试时尽量用第三种
相关推荐
05Kevin1 小时前
lk每日冒险题--数据结构6.27
算法
To_OC12 小时前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安17 小时前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者19 小时前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent
北域码匠1 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
卷无止境1 天前
C++ 的Eigen 库全解析
c++
卷无止境1 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端