描述
给定一个数组,将数组中的元素向右移动 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]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array/
求解
// 最大公约数,最小公倍数
template<typename T>
T gcd(T a, T b) {
while (a % b != 0) {
T c = a % b;
a = b;
b = c;
}
return b;
}
template<typename T>
T lcm(T a, T b) {
return a * b / gcd(a, b);
}
class Solution {
public:
// 方法一,原地交换实现原始版,空间复杂度O(N)原始版
void rotate_1(vector<int> &nums, int k) {
const int N = nums.size();
if (N < 2 || (k % N == 0)) {
// 少于2个元素或者翻转k是元素个数的倍数,不做任何动作
return;
}
int n = gcd(N, k);
for (int i = 0; i < n; ++i) {
int start = i;
int curPos = i;
int pre = nums[curPos];
int next = 0;
do {
int nextPos = (curPos + k) % N;
next = nums[nextPos];
nums[nextPos] = pre;
pre = next;
curPos = nextPos;
} while (curPos != start);
}
}
// 方法一优化版,原地交换实现代码优化版,空间复杂度O(N)原始版
void rotate_2(vector<int> &nums, int k) {
const int N = nums.size();
if (N < 2 || (k % N == 0)) {
// 少于2个元素或者翻转k是元素个数的倍数,不做任何动作
return;
}
int n = gcd(N, k);
for (int i = 0; i < n; ++i) {
int prev = nums[i];
int curPos = i;
do {
int nextPos = (curPos + k) % N;
// 通过每次的元素交换,将当前被覆盖的位置(nextPos)元素保存到prev中,待下次交换使用
std::swap(nums[nextPos], prev);
curPos = nextPos;
} while (curPos != i);
}
}
// 方法二,采用辅助数组空间
void rotate_usespace(vector<int> &nums, int k) {
const int N = nums.size();
if (N < 2 || (k % N == 0)) {
// 少于2个元素或者翻转k是元素个数的倍数,不做任何动作
return;
}
vector<int> record(N, 0);
for (int i = 0; i < N; ++i) {
int changePos = (i + k) % N;
record[changePos] = nums[i];
}
nums.assign(record.begin(), record.end());
}
// 方法三,采用STL算法实现
void rotate(vector<int> &nums, int k) {
const int N = nums.size();
if (N < 2 || (k % N == 0)) {
// 少于2个元素或者翻转k是元素个数的倍数,不做任何动作
return;
}
k = k % N; // 处理k > N的场景
std::rotate(nums.begin(), nums.begin() + (N - k), nums.end());
}
};