PS:算法并非原创,仅作个人学习记录使用,侵删。
题目描述
算法分析
看到题目的初步算法思路是:
将数组转化为类链表结构,用一个指示下标的指针,向右移动k个(超过k就k%n)个元素大小,然后从当前指针为开始指针,得到结果。但是没办法做到原地算法。
还有一种思路也比较简单:截取,根据k将数组截取为两部分,然后互换,依然没办法做到原地算法。
还有一种不算是我的思路,算是我记得的一种算法:三次逆转,按照k将数组截取为前后两部分,两部分分别在原数组空间逆转,之后再将整个数组再次逆转,三次逆转之后将会得到正确结果。这种算是原地算法,而且很巧妙。
后来看了大佬们的实现思路,还有一种思路:原数组元素交换,这样也可做到原地算法,只是交换的过程较为复杂,详见C++代码实现和python代码实现的两种循环交换方式。
**代码实现
【C】
/*
C++采用的算法也就是截取的思路,将数组截取为两部分,交换顺序存入临时数组,之后再载入原数组
*/
void rotate(int* nums, int numsSize, int k){
//nums为目标整型数组,numsSize表示数组长度,k表示移动幅度
if(numsSize>k){//如果移动的步数没有超过数组长度,
int *buf = (int *)malloc(numsSize*sizeof(int));//开辟新的存储空间
memcpy(buf, nums+(numsSize-k),k*sizeof(int));//buf前半段,存储原数组中后半段长度为k的数组
memcpy(buf+k,nums,(numsSize-k)*sizeof(int));//buf后半段,存储原数组中前半段长度为n-k的数组
memcpy(nums, buf,numsSize*sizeof(int));//buf中的数据转存入nums中
}
else{
k = k - numsSize;//相当于k %= numsSize。
rotate(nums, numsSize, k);//递归采用rotate函数
}
}
C语言参考网址
【C++】
/*
C++:循环交换
直接在原始数组上实现元素交换,不需要额外的存储空间,是一类原地算法。
将数组前后k个元素为一组进行交换,每次的前k个都不会重叠,每次的后k个基本上都是最后k个
*/
class Solution {
public:
void swap(vector<int> & nums, int i, int j){//交换函数,交换nums中的元素i和元素j
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
void rotate(vector<int>& nums, int k) {
if(k>=nums.size())
k%=nums.size();//控制实际的移动大小在一个数组长度范围内
if(k!=0){//如果需要移动的话
int n=nums.size();
for (int start = 0; start < nums.size() && k != 0; n -= k, start += k,k %= n) {
//start每次移动k步,在start和start+k之间的元素,需要和n-k和n之间的元素进行对应交换
/*举例子:
1 2 3 4 5 6 7 k=3
第一次循环:start=0,替换nums[0]和nums[4],nums[1]和nums[5],nums[2]和nums[6],得到的数组是:5 6 7 4 1 2 3
==>> n = 4, start = 3,k = 3( = 3%4)
第二次循环:start=3,替换nums[3]和nums[4],nums[4]和nums[5],nums[5]和nums[6],得到的数组是:5 6 7 1 2 3 4
==>> n = 1,start = 6,k = 0( = 3%1)
由于k=0,结束循环
*/
for (int i = 0; i < k; i++) {//每次分块对应交换
swap(nums, start + i, nums.size() - k + i);
}
}
}
}
};
C++参考网址
【java】
/*
java:三次逆转
将整体逆转,之后分组,两组元素分别进行逆转。是一个原地算法
需要自定义逆转的算法函数
*/
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;//缩小移动步数
//先整体后部分
reverse(nums, 0, nums.length - 1);//逆转整体
reverse(nums, 0, k - 1);//逆转前半段
reverse(nums, k, nums.length - 1);//逆转后半段
//如果先部分后整体的话,分段就是0~nums.length-k-1,num.lenght-k~num.lenght-1
}
public void reverse(int[] nums, int i, int j){//逆转nums[i]~nums[j]的元素
while(i < j){//首尾交换
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
j--;
}
}
}
java参考网址
【python】**
#
# python:循环交换
# 基本思想和C++的差不多,也是一类原地算法,但是由于是单个元素之间的比较,有些复杂
#
class Solution:
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: void Do not return anything, modify nums in-place instead.
"""
if k == 0 or len(nums) < 2:#不需要移动的情况
return
length = len(nums)#length表示数组长度
n, i, j = 0, 0, 0
temp1 = nums[0]#起始元素值
while n < length:#循环数组
#依然是循环交换的策略,不同的是:这里是单个元素之间的交换,而C++展示的是带着分块思想的整体交换
j = (j + k) % length
temp2 = nums[j]
nums[j] = temp1
temp1 = temp2
if i == j:
i = (i + 1) % length
j = i
temp1 = nums[i]
n += 1