题目
简要描述
给定一个数组,将数组中的元素向右移动 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]
解题
思路1
按照实例中给出的说明,每次向右移动一位,然后总共移动k次.最终提交结果超时。
代码
// 第一种:每次向右旋转1次,总共旋转k次
rotate_right(nums, k);
void rotate_right(vector<int> & nums, int k) {
int temp;
int size = nums.size();
for (int i = 0; i < k; i++) {
temp = nums[size - 1];
for (int j = size - 1; j > 0; j--) {
nums[j] = nums[j - 1];
}
nums[0] = temp;
}
}
思路2
在思路1的基础上,考虑到当向右移动位数大于一半时,改为向左移动总长度减去向右移动次数。最终结果超时。
代码
// 第二种:根据总长度和旋转值,如果旋转值大于一半的长度,将向右旋转转为向左旋转
k = k % nums.size();
if (k <= (nums.size() >> 1)) {
rotate_right(nums, k);
} else {
rotate_left(nums, nums.size() - k);
}
void rotate_right(vector<int> & nums, int k) {
int temp;
int size = nums.size();
for (int i = 0; i < k; i++) {
temp = nums[size - 1];
for (int j = size - 1; j > 0; j--) {
nums[j] = nums[j - 1];
}
nums[0] = temp;
}
}
void rotate_left(vector<int> & nums, int k) {
int temp;
int size = nums.size();
for (int i = 0; i < k; i++) {
temp = nums[0];
for (int j = 0; j < size - 1; j++) {
nums[j] = nums[j + 1];
}
nums[size - 1] = temp;
}
}
思路3
题目要求是向右移动 k 个位置,那么就将每个元素向右移动k个位置,并且题目给出了要求使用了O(1)的空间,就可以将第1个元素移动到第k个位置后,记录下第k个位置的元素的值,然后将第k个位置的值,继续向右移动k个位置。
这样存在的问题是,如果是4个元素,向右移动两个位置,将会出现0和2的位置的元素在移动,1和3的位置的元素不变。这个时候,在0,2位置移动完之后,就需要手动移动到1位置再进行一次移动。
需要手动调整位置的次数与最大公因数有关,每一轮移动的元素数量为数组大小除以最大公因数的值。
代码
// 第三种:跳跃式旋转,每个元素直接向后移动k位,此时需要考虑长度与移动位置的最大公因数
// 如果为1的话,表示不会出现重复,一轮便可以
// 如果不为1的话,表示会出现重复,需要最大公因数轮次才可以
// 举例 5个元素,右移3位
// 1,2,3,4,5 -> 1,2,3,1,5 (4) -> 1,4,3,1,5 (2) -> 1,4,3,1,2 (5) -> 1,4,5,1,2 (3) -> 3,4,5,1,2 (1)
// 这样一轮便可以搞定,如果是4个元素,右移2位
// 1,2,3,4 -> 1,2,1,4 (3) -> 3,2,1,4 (1) 一轮之后,需要调整起始点为第二个元素
// 3,2,1,4 -> 3,2,1,2 (4) -> 3,4,1,2 (2) 两轮之后,就可以结束
rotate_jump(nums, k % nums.size());
void rotate_jump(vector<int> & nums, int k) {
// temp1 记录要放进去的元素
// temp2 记录需要暂时取出来的元素
int temp1, temp2;
int size = nums.size();
// 计算最大公因数,也就是循环轮次
int gcd_val = gcd(size, k);
// 计算每轮循环需要进行多少次跳跃
int times = size / gcd_val;
int pos, next_pos;
for (int i = 0; i < gcd_val; i++) {
pos = i;
temp1 = nums[pos];
for (int j = 0; j < times; j++) {
next_pos = (pos + k) % size;
temp2 = nums[next_pos];
nums[next_pos] = temp1;
pos = next_pos;
temp1= temp2;
}
}
}