给定一个数组,将数组中的元素向右移动 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) 的原地算法。
题解:
LeetCode基础题,暴力模拟也可以过,但是我觉得这题暴力过就失去了意义,做法有很多,下面是两种思路,经测试两种思路的时间复杂度都差不多都是O(1),而不是暴力模拟的k*O(1)
第一种:
将数组分为两部分,假设数组长度是n,第一组为[0,n-k-1],另外一组为[n-k,n-1],因为后面那组实际上是要平移到最前面,而前面那组是要平移到后面,即两组位置进行平移交换,那么我们可以经过三次翻转得到,第一次将第一组原地首尾互置,第二次将第二组原地首尾互置,最后将整个数组首尾互置,就得到了我们要的结果。
第二种:
假设数组长度为n,那么我们可以将数组分为n/k组,第一重循环遍历0到k-1,第二重循环通过数学推算出其他组相同位置的下标,比如n=6,1 2 3 4 5 6,k=2时,那么1 3 5就算作是每一组的相同位置,进行替换位移,最后每个元素遍历一次就可以完成换位,但是需要注意,当n/k不为整数时,需要将多出来的不成一组的位置进行特殊处理。。这部分还是很麻烦的,调了好几次才调好,代码也多了不少,最后击败了百分之99.97的人。。
本题需要注意的是数据贼坑,n,k都可以为0,k也可以大于n,需要特判和特殊处理。
我用的是第二种,代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
if(nums.size()==0||k==0)
return;
k=k%nums.size();
if(k==0)
return;
int s=nums.size();
vector<int>tmp;
int n=s/k;
int p=k*(s/k);
for(int i=0;i<k;i++)
{
int tmp1=nums[i];
int tmp2;
//printf("%d变为%d\n",nums[i],nums[(s-(k-i))%s]);
nums[i]=nums[(s-(k-i))%s];
for(int j=1;j<n;j++)
{
tmp2=nums[(i+j*k)%s];
//printf("%d变为%d\n",nums[(i+j*k)%s],tmp);
nums[(i+j*k)%s]=tmp1;
tmp1=tmp2;
if(i+(j+1)*k<s&&i+(j+1)*k>=p)
{
tmp.push_back(tmp1);
}
}
int j=0;
if(i+(j+1)*k<s&&i+(j+1)*k>=p)
{
tmp.push_back(tmp1);
}
}
if(s%k!=0)
for(int i=0;i<tmp.size();i++)
{
nums[p+i]=tmp[i];
}
}
};