复杂度算法题——旋转数组(三种思路)

旋转数组---题目链接

https://leetcode.cn/problems/rotate-array/description/

思路1-循环嵌套

  1. 先创建一个临时存储变量tmp来存储数组的最后一个元素
c 复制代码
int tmp = nums[numsSize - 1];
  1. 用将 数组nums倒数第二位的元素 移动到 数组最后一位 ,以此类推,运用循环次将数组所有元素向后移动⼀位,就完成了第一次元素后移
c 复制代码
 for (int i = numsSize - 1; i > 0; i--)
        {
            nums[i] = nums[i - 1];
        }
  1. 完成一次后移后,需要将临时存储变量tmp的值赋给nums数组首位元素
c 复制代码
nums[0] = tmp;
  1. 给循环再套一层循环,循环K次将数组所有元素向后移动⼀位
c 复制代码
while (k--)
    {
        int tmp = nums[numsSize - 1];
        for (int i = numsSize - 1; i > 0; i--)
        {
            nums[i] = nums[i - 1];
        }
        nums[0] = tmp;
    }

完整代码

c 复制代码
void rotate(int* nums, int numsSize, int k) 
{
    // 循环k次,每次将数组向右移动1位
    while (k--)
    {
        // 保存数组最后一个元素(移动后会成为新的第一个元素)
        int tmp = nums[numsSize - 1];
        
        // 从倒数第二个元素开始,依次将前一个元素的值赋给当前位置
        // 实现所有元素整体后移1位
        for (int i = numsSize - 1; i > 0; i--)
        {
            nums[i] = nums[i - 1];
        }
        
        // 将之前保存的最后一个元素放到数组第一个位置,完成1次右移
        nums[0] = tmp;
    }
}

该思路的时间复杂度 O(N^2),空间复杂度O(1)(在力扣没办法成功通过,时间复杂度过高)

思路2-空间换时间

申请新数组空间,先将后k个数据放到新数组中,再将剩下的数据挪到新数组中

  1. 创建一个相同空间大小新数组new来存储nums数组向右移动后k次后的数据
c 复制代码
int new[numsSize];
  1. 仔细观察移动前和移动后的数组,发现(i + k)可以确定原数组第i个元素右移k位后的新位置

    但是当 k > numsSize时,例如
c 复制代码
int nums = { 1 };
当 k = 2 时 ,(i + k) 一定会造成数组越界

这时我们就可以让(i + k)numsSize 取模%将大小限制在 0 ~ numsSize 之间,即 (i + k) % numsSize ,这样就不会造成数组越界了

第二部分的代码我们可以这样写

c 复制代码
  for (int i = 0; i < numsSize; i++)
  {
      new[(i + k) % numsSize] = nums[i];
  }
  1. 最后只要将new数组中轮转后的结果复制回原数组nums即可
c 复制代码
 for (int i = 0; i < numsSize; i++)
 {
     nums[i] = new[i];
 }

完整代码

c 复制代码
void rotate(int* nums, int numsSize, int k) 
{
    // 创建一个与原数组大小相同的临时数组,用于存储轮转后的结果
    int new[numsSize];

    // 遍历原数组,计算每个元素轮转后的位置并放入临时数组
    // 公式 (i + k) % numsSize 用于确定原数组第i个元素右移k位后的新位置
    // 取余操作处理了k大于数组长度的情况(避免无效轮转)
    for (int i = 0; i < numsSize; i++)
    {
        new[(i + k) % numsSize] = nums[i];
    }

    // 将临时数组中轮转后的结果复制回原数组,完成原地更新
    for (int i = 0; i < numsSize; i++)
    {
        nums[i] = new[i];
    }
}

该思路的时间复杂度O(N),空间复杂度O(N)

思路三-反转

先前n-k个反转,再后k个反转,最后整体反转

证明反转可行性:

假设要把 AB 变成 BA(A是前半段,B是后半段):

先反转A:得到 A'B(A'是A的反转)

再反转B:得到 A'B'(B'是B的反转)

最后反转整个数组:A'B' 整体反转后,会变成 BA(因为B'反转回B,A'反转回A,且顺序交换)

例如:

c 复制代码
前n-k个反转:4 3 2 1 5 6 7  
后k个反转  :4 3 2 1 7 6 5  
整体反转   :5 6 7 1 2 3 4
  1. 首先需要写一个反转函数reverse,函数需要的参数有:需要反转的数组nums、数组需要反转元素的范围,即元素下标的起点,终点
c 复制代码
void reverse(int* nums, int start, int end)
{

}
  1. 运用while循环,来实现反转,当start > end 时,循环结束
c 复制代码
void reverse(int* nums, int start, int end)
{
    while(start < end)
    {
        int tmp = nums[start];
        nums[start] = nums[end];
        nums[end] = tmp;

        start++;
        end--;
    }
}
  1. 在rorate函数中运用reverse函数
    这里要注意 k 的大小不能使数组越界,可以用k = k % numsSize;语句来限制k的范围在0 ~ numsSize

先数组下标为0 ~ (numsSlze - k - 1)的元素反转

c 复制代码
    reverse(nums, 0, numsSize - k - 1);

再数组下标为(numsSlze - k ) ~ (numsSlze - 1)的元素反转

c 复制代码
    reverse(nums, numsSize - k, numsSize - 1);

最后整体反转,即数组下标为0 ~ (numsSlze - 1)的元素反转

c 复制代码
reverse(nums, 0, numsSize - 1);

完整代码

c 复制代码
// 辅助函数:反转数组中从start到end(包含两端)的元素
// start:反转的起始下标
// end:反转的结束下标
void reverse(int* nums, int start, int end)
{
    // 当start小于end时,交换两端元素并向中间移动
    while (start < end)
    {
        // 临时变量保存start位置的元素
        int tmp = nums[start];
        // 交换start和end位置的元素
        nums[start] = nums[end];
        nums[end] = tmp;

        // 起始下标后移,结束下标前移,缩小反转范围
        start++;
        end--;
    }
}

// 主函数:将数组nums向右轮转k个位置
// nums:待轮转的数组
// numsSize:数组的长度
// k:轮转的位数
void rotate(int* nums, int numsSize, int k)
{
    // 处理k大于数组长度的情况:轮转n(数组长度)位相当于没轮转,取余后得到有效轮转位数
    k = k % numsSize;
    
    // 第一步:反转数组的前半部分(0到numsSize - k - 1)
    // 前半部分是指不需要移到开头的元素(共numsSize - k个)
    reverse(nums, 0, numsSize - k - 1);
    
    // 第二步:反转数组的后半部分(numsSize - k到numsSize - 1)
    // 后半部分是指需要移到开头的元素(共k个)
    reverse(nums, numsSize - k, numsSize - 1);
    
    // 第三步:反转整个数组(0到numsSize - 1)
    // 经过前两次局部反转后,整体反转即可得到轮转结果
    reverse(nums, 0, numsSize - 1);
}

该思路的时间复杂度O(N),空间复杂度O(1)

相关推荐
To_OC11 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
用户9385156350716 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC17 小时前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法
饼干哥哥18 小时前
Reddit VOC调研太慢?搭一个AI专家团队半小时洞察任何品类|以猫用饮水机为例
人工智能·算法·ai编程
地平线开发者19 小时前
Transformer模型部署之性能优化指南
算法
地平线开发者19 小时前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶
半个落月1 天前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
小月土星1 天前
JavaScript 快速排序:从 pivot、双指针到分治思想
javascript·算法·面试
小月土星1 天前
JavaScript 递归入门:从 1 到 n 求和,再到数组扁平化
javascript·算法·面试