旋转数组---题目链接
https://leetcode.cn/problems/rotate-array/description/

思路1-循环嵌套
- 先创建一个临时存储变量tmp来存储数组的最后一个元素
c
int tmp = nums[numsSize - 1];
- 用将 数组nums倒数第二位的元素 移动到 数组最后一位 ,以此类推,运用循环次将数组所有元素向后移动⼀位,就完成了第一次元素后移
c
for (int i = numsSize - 1; i > 0; i--)
{
nums[i] = nums[i - 1];
}
- 完成一次后移后,需要将临时存储变量tmp的值赋给nums数组首位元素
c
nums[0] = tmp;
- 给循环再套一层循环,循环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个数据放到新数组中,再将剩下的数据挪到新数组中
- 创建一个相同空间大小新数组new来存储nums数组向右移动后k次后的数据
c
int new[numsSize];
- 仔细观察移动前和移动后的数组,发现
(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];
}
- 最后只要将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
- 首先需要写一个反转函数reverse,函数需要的参数有:需要反转的数组nums、数组需要反转元素的范围,即元素下标的起点,终点
c
void reverse(int* nums, int start, int end)
{
}
- 运用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--;
}
}
- 在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)