🚗题目描述
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
Q:你能用空间复杂度O(1)原地解决这个问题吗?
示例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]
🧀 解法1:暴力求解,旋转k次
利用temp和end,end为数组最后一位,将nums[j]存放在temp中,将end→nums[j],经过一次旋转后得到 7 1 2 3 4 5 6,旋转三次后即得到 5 6 7 1 2 3 4
时间复杂度为:O(N)=k*N
空间复杂度为:O(N)=1
图释:
代码实现如下:
void Rotate(int *nums,int numsize,int k){
k%=numsize;
for(int i=0;i<k;i++)
{
int end = nums[numsize-1];
int temp;
for(int j=0;j<numsize;j++)
{
temp=nums[j];
nums[j]=end;
end=temp;
}
}
}
int main(){
int nums[7],k;
for(int i=0;i<7;i++)
{
cin>>nums[i];
}
cout<<"k=";
cin>>k;
Rotate(nums,7,k);
for(int i=0;i<7;i++)
{
cout<<nums[i]<<" ";
}
return 0;
}
🧀 解法2:巧妙逆置算法
思路:将长度为numsize数组分为两部分,一部分为[0,numsize-k-1],另一部分为[k,numsize-1],分别进行转置,得到数组再全部进行转置即可巧妙得到题目要求的
时间复杂度:O(N)
空间复杂度:O(1)
图释:
代码实现如下:(需要先写转置代码)
void Reverse(int *nums,int left,int right)
{
while(left<right){
int temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
left++;
right--;
}
}
void Rotate(int *nums,int numsize,int k){
k%=numsize;
Reverse(nums,0,numsize-k-1);
Reverse(nums,numsize-k,numsize-1);
Reverse(nums,0,numsize-1);
}
int main(){
int nums[7],k;
for(int i=0;i<7;i++)
{
cin>>nums[i];
}
cout<<"k=";
cin>>k;
Rotate(nums,7,k);
for(int i=0;i<7;i++)
{
cout<<nums[i]<<" ";
}
return 0;
}
🧀 解法3:拷贝法(空间复杂度不为O(1))
创建一个新的数组,将原数组的后k个数即[numsize-k,numsize-1]拷贝到新数组的前面[0,k-1],原数组前面的数拷贝到新数组后面[k,numsize-1]。
时间复杂度为:O(N)
空间复杂度为:O(N)
图释:
代码实现如下:
void Rotate(int *nums,int numsize,int k)
{
k%=numsize;
int newnums[numsize];
for(int i=0;i<=numsize-k;i++)
{
newnums[i]=nums[numsize-k+i];
}
for(int i=numsize-k+1,j=0;i<numsize;i++,j++)
{
newnums[i]=nums[j];
}
for(int i=0;i<7;i++)
{
cout<<newnums[i]<<" ";
}
}
int main(){
int nums[7],k;
for(int i=0;i<numsize;i++)
{
cin>>nums[i];
}
cout<<"k=";
cin>>k;
Rotate(nums,7,k);
return 0;
}
🧀 解法4:环状替换法
从起点开始,每次以K为单位进行跳跃式的替换,每个元素都会进行一次替换,进行了N次替换,即完成了数组的旋转;
注意点:
1.当下标+K大于数组长度的时候,就需要减去长度回到”起点“处;
2.回到起点后,判断回来的位置是否和出发的位置重复,如果不重复,那么一直进行下去,直至完成N此跳跃,形成一个环圈;
如果重合了,每次回到起点后就将起点完后挪动一个位置,直至完成N次跳跃;时间复杂度为:O(N)
空间复杂度为:O(1)
代码实现如下:
void Rotate(int* nums, int numsize, int k){
k %= numsize;
int count = 0;
int previous = nums[0];//先将出发点元素储存起来,和遍历法类似
int lable = 0;//标记出发点,用于判断返回点是否和出发点重合
int sub = k;
while (count < numsize)//count=元素个数时跳出循环,即完成了size次交换
{
if (sub < numsize)//跳跃之后没有超过数组长度
{
int temp = nums[sub];
nums[sub] = previous;//进行替换
count++;
previous = temp;
sub += k;//跳跃K个距离
}
else//即将完成一个系列的转换
{
int temp = nums[sub - numsize];//超过了数组长度,回到起点
nums[sub - numsSize] = previous;
count++;
previous = temp;
if (sub - numsize == lable)//回到起点的位置和出发位置相同
{
sub = sub - numsize + 1;
lable = sub;//继续标记出发位置
previous = nums[sub];//重新储存起点
sub += k;
}
else
{
sub = sub - numsize + k;
}
}
}
}
---------以上为四种方法,仅供参考~