题目链接: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]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
思路一:
先保存下标numsSize - 1的元素,将前numsSize - 1个元素往后移动一位,最后将保存的元素放在数组第一个位置。
代码实现:
void rotate(int* nums, int numsSize, int k) {
while(k--){
int tmp = nums[numsSize - 1];
for (int i = numsSize - 1;i>0;i--){
nums[i] = nums[i - 1];
}
nums[0] = tmp;
}
}
分析:
由于这里存在双循环,时间复杂度为O(n^2),空间复杂度O(1),还需要继续优化思路。
思路二:创建新数组
在这里,用malloc动态申请一个新数组arr,用来存放轮转后的数组,将原数组中的下标为i的元素放在arr中下标为(i+k)%numsSize的位置,这里%numsSize的原因时i+k在遇到要向右轮转的元素时会超出numsSize,这里直接让其重新回到数组头部。
由于函数返回的是数组nums,最后再将arr中的元素复制到nums即可。
代码实现:
void rotate(int* nums, int numsSize, int k) {
int* arr = (int*)malloc(numsSize*sizeof(int));
for (int i = 0;i<numsSize;i++)
{
arr[(i+k)%numsSize] = nums[i];
}
for (int i = 0;i<numsSize;i++)
{
nums[i] = arr[i];
}
}
分析:
代码中用了两个循环,时间复杂度为O(n),相较于思路一,运行时间缩短,但相较于思路一,空间复杂度提升到了O(n),这是一种时间换空间的方法,那么是否有办法让空间复杂度降为O(1)呢?
思路三:三次逆置
依旧以轮转三次为例:
第一次逆置:将前n-k个数据逆置
第二次逆置:将后k个数据逆置
第三次逆置:整体逆置
void reverse(int* nums,int left,int right)
{
while(left < right)
{
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
由于需要用到三次逆置,为了不让代码冗余,这里我们封装一个用来逆置的函数,参数为数组,数组中需要逆置的部分的头部和尾部的下标。
k %= numsSize;
若k值超过了numsSize,那么数组会重复轮转,所以我们这里模上一个numsSize。
代码实现:
void rotate(int* nums, int numsSize, int k) {
k %= numsSize;
//三次逆置
//前numsSize-k个数据逆置
reverse(nums,0,numsSize-k-1);
//后k个数据逆置
reverse(nums,numsSize-k,numsSize-1);
//整体逆置
reverse(nums,0,numsSize-1);
}
分析:
思路三中,空间复杂度降为了O(1),时间复杂度依旧为O(n),在思路二的基础上再次进行了优化,不再借助新数组,实现了一个较为完善的方案。