题目:
给定一个整数数组
nums
,将数组中的元素向右轮转k
个位置,其中k
是非负数。
示例1:
输入: nums = [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:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
思想1 暴力求解
从右边开始,每轮移动一次,移动k轮,但是这种解法最坏的情况是k等于numsSize - 1,每轮又要移动numsSize - 1次。时间复杂度为O(k*n),或者说是O(n^2)
画图分析:
代码实现:
void rotate(int* nums, int numsSize, int k){ if(k > numsSize) { k %= numsSize; } while(k) { int tmp = nums[numsSize - 1]; int i = 0; for(i = numsSize - 2;i >= 0;i--) { nums[i + 1] =nums[i]; } nums[0] = tmp; k--; } }
我们以这种方法去写固然可行,但是放入leetcode之中,再去提交的时候,只通过了37/38个测试用例,leetcode检测过于严格,通过不了,那么这种方式去写就不太合适了,有没有更加好的解法思想呢?我们往下看。
思想2 三次倒置
第1步.我们从k处分隔数组,分为左右两边,各自进行倒置即可;
第2步.对整个数组进行倒置,这样子就会让最开始想要翻转的数组个数挪动到前面了。
注⚠️:也可以先进行第二步,再进行第1步,此方法无先后顺序,最终都能达到效果。
画图分析:
注⚠️: k 必须要保证小于numsSize,否则提交代码会出现以上图片中的执行错误,原因是数组出现越界,将执行结果,numsSize为1,k = 2,代入第一个reverse函数,结果为-1,故会出现数组越界,此外,k需要避免过大,进行多次无效的重复运算,以示例1为例,数组翻转8次就相当于数组翻转1次。
代码实现:
void reverse(int* nums,int left,int right) { while(left < right) { int tmp = nums[left]; nums[left] = nums[right]; nums[right] = tmp; left++; right--; } } void rotate(int* nums, int numsSize, int k){ if(k > numsSize) { k %= numsSize;//去掉if条件判断也行 } reverse(nums,numsSize - k,numsSize - 1);//右边倒置 reverse(nums,0,numsSize - k - 1);//左边倒置 reverse(nums,0,numsSize - 1);//整体倒置 }
思想3 memcpy拷贝数组
我们利用malloc申请一块为numsSize元素*int类型大小的内存空间,我们从k处分隔原数组,利用memcpy函数先将后面的k个元素拷贝到内存空间,再次将前numsSize - k个元素拷贝到k个元素的后面,最后将内存空间的numsSize*sizeof(int)个字节空间拷贝到原数组之中。
对memcpy函数不熟悉的小伙伴,这里放出库函数的使用规则,更详细内容,可以在cplusplus 官网进行搜索查看哦
代码实现:
void rotate(int* nums, int numsSize, int k){ if(k > numsSize) { k %= numsSize; } int* tmp = (int*)malloc(sizeof(int) * numsSize); memcpy(tmp,nums + numsSize - k,sizeof(int) * k); memcpy(tmp + k,nums,(numsSize - k) * sizeof(int)); memcpy(nums,tmp,sizeof(int) * numsSize); // free(tmp); //tmp = NULL }