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

旋转数组---题目链接

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个反转,最后整体反转

证明反转可行性:

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

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

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

最后反转整个数组:[A'][B'] 整体反转后,会变成 [B][A](因为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)

相关推荐
tankeven2 小时前
HJ151 模意义下最大子序列和(Easy Version)
c++·算法
Sirens.2 小时前
对顺序表以及双向链表的理解
数据结构·链表
不光头强2 小时前
力扣78子集题解
算法·leetcode·深度优先
独断万古他化2 小时前
【算法通关】二叉树中的深搜:DFS 递归解题套路
算法·二叉树·深度优先·dfs·递归
㓗冽2 小时前
2026.03.27(第三天)
数据结构·c++·算法
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章44-直线卡尺
图像处理·人工智能·opencv·算法·计算机视觉
Magic--2 小时前
经典概率题:飞机座位分配问题(LeetCode 1227)超详细解析
算法·leetcode·职场和发展
Rooting++2 小时前
C 位域的作用
c语言
always_TT2 小时前
C语言中的“副作用”是什么?
c语言·开发语言