解法一:
1 -解题思路
假设从下标0开始遍历,一直到遍历完成回到下标0完成轮转。
回到下标0时,可能中间有些元素并未遍历,此时从下标1开始依次遍历,一直到遍历完成回到下标1,如此循环往复,直到所有元素都遍历过了
假设一次遍历了b个元素,遍历了a圈,那么就有an=bk;an是n,k的公倍数,因为要使遍历圈数尽可能小,因此a为n,k最小公倍数(lcm(n,k)),b=an/k=lcm(n,k)/k;
那么一共要遍历n/b次,即
![](https://i-blog.csdnimg.cn/blog_migrate/2a3465ef5bf81baf3601df1fa5a9455a.png)
gcd(n,k)为最大公约数
例如:假定数组a[9]={8,5,9,6,2,1,1,4,5},n=9,k=3,最大公约数为start=3,因此要遍历3次
![](https://i-blog.csdnimg.cn/blog_migrate/19054fac9890af550004a94deaf4490e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6ff2159b81365bc7929b0b471ce5fcd0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7ad3b78b1ea8267ed282caff9cea7066.png)
2 -解题方法
①欧几里得算法,辗转相除法求最大公约数
795/45=17......30
45/30=1......15
30/15=2......0
得出最大公约数是15
3 -Code
int gcd(int a,int b)//求numsSize和k的最大公约数
{
return b?gcd(b,a%b):a;//①b为0时,返回:后面的值②b为1时,返回:前面的值
} //欧几里得算法,辗转相除求最大公约数
void swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void rotate(int* nums, int numsSize, int k){
k=k%numsSize; //k求余,排除移动次数大于元素个数;
int count=gcd(numsSize,k); //求numsSize和k的最大公约数
for(int start=0;start<count;start++)//循环count次
{
int current=start; //本次循环开始要移动的元素下标
int pre=nums[start]; //保留本次循环开始要移动的元素
do{
int next=(current+k)%numsSize;//目标位置
swap(&pre,&nums[next]); //目标位置与本次要移动元素交换数据
current=next; //移动元素移动
}while(start!=current);
}
}
4 -算法分析
时间复杂度O(n^2)
空间复杂度O(1)
5 -总结
①辗转相除法求最大公约数
②两个数交换的代码多次使用可以单独写一个函数,这样代码简洁易读
③return b?gcd(b,a%b):a;b为0时,返回:后面的值;b为1时,返回:前面的值
解法二:
1 -解题思路
数组轮转过程可以分为三步
①数组下标0~numsSize-1翻转
②数组下标0~k-1翻转
③数组下标k~numsSize-1翻转
例如:假定数组a[9]={8,5,9,6,2,1,1,4,5},n=9,k=3
![](https://i-blog.csdnimg.cn/blog_migrate/ac21fc21b637abb304b26505811e4b74.png)
2 -解题方法
依次翻转元素即可,这里要注意翻转时使用while循环,并且用首尾指针记录元素下标,较简洁
while(start<end)
{
change(&nums[start],&nums[end]);
start+=1;
end-=1;
}
注意:而不是用for循环控制,这样下标很难表示,利用while只要写一个通用的函数就可以了
for(i=0;(i<numsSize/2)%numsSize;i++)
{
change(&nums[i],&nums[numsSize-1-i]);
}
3 -Code
void change(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void reverse(int *nums,int start,int end)
{
while(start<end) //轮转数组
{
change(&nums[start],&nums[end]);
start+=1;
end-=1;
}
}
void rotate(int* nums, int numsSize, int k){
k%=numsSize; //k求余,排除移动次数大于元素个数;
reverse(nums,0,numsSize-1);
reverse(nums,0,k-1);
reverse(nums,k,numsSize-1);
}
4 -算法分析
时间复杂度:O(n),每个元素被翻转两次O(2n)
空间复杂度:O(1),没有使用额外的空间
5 -总结
在考虑数组翻转时,首选while循环,并且对于重复的代码,可以封装成一个函数。
k对numsSize求余。