
对于这题,一个最直观、最简单的方法是:开一个新数组,将原数组的元素按新顺序填入新数组。
这样会有O(n)O(n)O(n)的空间复杂度。
还有什么办法,能够使空间复杂度降至O(1)O(1)O(1)?
考察一下整个交换过程,可以发现,其可以被拆解为许多"环"交叠在一起。
对任意起点 iii,按 i→i+k→i+2k→...(modn)i → i+k → i+2k → ... (mod n)i→i+k→i+2k→...(modn) 走,迟早回到 iii,形成一个环。如下:
对于 nums = [1, 2, 3, 4, 5, 6, 7, 8, 9], k = 3
令start = 0
首先是 1→4→7→11 \rightarrow 4 \rightarrow 7 \rightarrow 11→4→7→1,发现回到起点,结束这次操作
此时 nums = [7 , 2, 3, 1 , 5, 6, 4 , 8, 9]
随后start++
继续操作:2→5→8→22 \rightarrow 5 \rightarrow 8 \rightarrow 22→5→8→2
此时 nums = [7, 8 , 3, 1, 2 , 6, 4, 5 , 9]
随后start++
继续操作:3→6→9→33 \rightarrow 6 \rightarrow 9 \rightarrow 33→6→9→3
此时 nums = [7, 8, 9 , 1, 2, 3 , 4, 5, 6 ]
操作完成
设 g=gcd(n,k)g = gcd(n, k)g=gcd(n,k),则:
环的个数 = ggg,而每个环的长度 = n/gn / gn/g
但除此以外,还有另一种方法。
把数组看成两段拼接 A∣BA | BA∣B:其中 A=nums[0..n−k−1]A = nums[0..n-k-1]A=nums[0..n−k−1],B=nums[n−k..n−1]B = nums[n-k..n-1]B=nums[n−k..n−1]。
右旋 kkk 位的目标是得到 B∣AB | AB∣A。
用反转的性质:
先整体反转 AB→(AB)R=BRARA B → (A B) ^ R = B ^ R A ^ RAB→(AB)R=BRAR
再反转前 kkk 个(原来的 BRB^RBR)得到 BBB
再反转后 n−kn-kn−k 个(原来的 ARA^RAR)得到 AAA
最终就是 B∣AB | AB∣A。
三种方法的代码如下:
cpp
class Solution {
public:
void rotate(vector<int>& nums, int k) {
// method 1:
// int n = nums.size();
// vector<int> ans(n);
// for(int i = 0;i<n;i++){
// ans[(i + k)%n] = nums[i];
// }
// ranges::swap(nums, ans);
// method 2:
// int n = nums.size();
// k = k % n;
// int g = gcd(n, k);
// for (int i = 0; i < g; ++i) {
// int current = i;
// int prev = nums[i];
// while (true) {
// int next = (current + k) % n;
// swap(nums[next], prev);
// current = next;
// if(current == i) break;
// }
// }
// method 3:
// int n = nums.size();
// k = k % n;
// ranges::reverse(nums);
// reverse(nums.begin(), nums.begin() + k);
// reverse(nums.begin() + k, nums.end());
}
};