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. 三种方法都要掌握,但面试时尽量用第三种
相关推荐
灰色小旋风2 小时前
力扣第八题C++ 字符串转换整数
c++·算法·leetcode
melonbo2 小时前
C++ 中用于模块间通信的设计模式
开发语言·c++·设计模式
进击的雷神2 小时前
请求频率限制、嵌套数据结构、多目录聚合、地址字段重构——K展爬虫四大技术难关攻克纪实
数据结构·爬虫·python·重构
@––––––2 小时前
力扣hot100—系列9—图论
算法·leetcode·图论
pp起床2 小时前
图论 | part01
算法·深度优先·图论
luckycoding2 小时前
3676. 碗子数组的数目
算法·游戏·深度优先
Qt程序员2 小时前
Linux .so 动态库:底层原理
linux·c++·内核·动态库
灰色小旋风2 小时前
力扣第十题C++正则表达式匹配
c++·leetcode·正则表达式
乌萨奇也要立志学C++2 小时前
【Linux】线程池(一)C++ 手写线程池:基于策略模式实现高性能日志模块
linux·c++·策略模式