https://leetcode-cn.com/problems/rotate-array/
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入:[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:
输入: [-1,-100,3,99]
和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的原地算法。
解法1:记录最后一个元素,将除最后一个元素外的所有元素后移一格,首元素赋值为尾元素,重复k次
void rotate(int* nums, int numsSize, int k) {
int p = nums[numsSize-1];
while(k--){
p = nums[numsSize-1];
for(int i = numsSize-1;i>0;i--){
nums[i] = nums[i-1];
}
nums[0] = p;
}
}//空间复杂度O(1)时间复杂度O(k*n)
解法2:跳跃赋值法
请先看以下几个例子:
[1,2,3,4,5,6,7] 和 k = 3
若从首位置开始,间隔k跳跃赋值,那么顺序是1->4->7->3->6->2->5->1,刚好通过n次遍历完数组,那么只需将前一个值赋给后一个,就可以完成数组变换,而数组长度与k公因子为1.
[1,2,3,4,5,6] 和k=4
1->5->3->1
2->6->4->2
通过两次循环可以将所有元素赋值更换,同样经过n次操作,而数组长度numsSize与k的公因子为2.
综上所述,我们只需考虑数组长度与k的公因子是否大于1这两种情况(实际上两种情况可以合并成一种情况)。
int gcd(int a,int b);
void rotate(int* nums, int numsSize, int k) {
int next = 0,pre=0,gcd1=0;
if(k==0) return ;
if(numsSize==1) return ;
gcd1 = gcd(numsSize,k);
for(int i = 0;i < gcd1;i++) //控制循环次数
{
pre = nums[i];
next = nums[i];
for(int j = i,count=0;count<numsSize/gcd1;count++){//每次循环将前值赋给当前值
next = nums[j]; //保存当前值
nums[j] = pre; //赋值
pre = next;
j = (j+k)%numsSize; //通过取模操作,控制下标不越界
}
nums[i] = next; //将未处理的首元素赋值
}
}
int gcd(int a,int b){ //辗转相除法求最大公因子
int rem=0;
while(1){
rem = a%b;
if(rem==0) return b;
else{
a = b;
b = rem;
}
}
}//求最大公因子
方法3:翻转法
[1,2,3,4,5,6]和k = 2
将[1,2,3,4]翻转为[4,3,2,1],将[5,6]翻转为[6,5]
这样数组变成[4,3,2,1,6,5]
再总体翻转
得到[5,6,1,2,3,4]
void reserve(int* nums,int left,int right);
void rotate(int* nums, int numsSize, int k) {
if(k==0 || numsSize==1 || (k%numsSize==0 && k!=1)) return; //排除特殊情况
k %= numsSize; //缩小范围
reserve(nums,0,numsSize-1); //函数位置不要任意改变
reserve(nums,0,k-1);
reserve(nums,k,numsSize-1);
}
void reserve(int* nums,int left,int right){
if(left>=right) return;
int temp;
while(left<right){
temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++,right--;
}
}