思路一:额外数组
额外分配一个数组存储轮转后的数组,然后再将轮转后的结果复制到原数组中
class Solution {
public:
void rotate(vector<int>& nums, int k) {
vector<int> result;
for(int i = 0; i < nums.size(); i++){
if(i + k < nums.size()){
result[i + k] = nums[i];
}
else{
result[i + k - nums.size()] = nums[i];
}
}
for(int i = 0; i < nums.size(); i++){
nums[i] = result[i];
}
}
};
最开始根据思路一写的代码是这个,但是执行时超时了,所以又改成了下述优化代码:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> result(n);
//result.reserve(n);
for(int i = 0; i < n; i++){
result[(i + k) % n] = nums[i];
}
nums.assign(result.begin(), result.end());
}
};
在初始化result数组时,使用了reserve方法使其错误分配空间 。
原因是:
vector<int> result();是一个 空的构造函数调用,它定义了一个没有任何元素的vector,也就是一个空的vector<int>。reserve(n)方法会为result预分配内存空间,以便在将来添加最多n个元素时不会触发多次重新分配内存。注意,reserve(n)只是预分配空间,并不会改变vector的实际元素数量。vector<int> result(n);这个构造函数会创建一个大小为
n的vector,并将其中每个元素初始化为0(对于基本类型int)。这意味着:result.size()会返回n,即vector中有n个元素。每个元素的初始值是0,如果没有特别的初始化参数,vector<int>的元素会使用默认构造函数进行初始化也就是说,后者可以直接分配空间并初始化元素;但前者可能在动态添加元素时更灵活,但不初始化元素
-
时间复杂度: O(n)
-
空间复杂度: O(n)
思路二:三次翻转
先对原数组整个进行翻转,然后翻转前 k 个元素,最后翻转后 n-k 个元素。
数组的旋转 本质上是将数组的最后
k个元素移动到数组的前面,其他元素向后移动。通过翻转,我们能将数组的顺序转换为反向的,并且逐步恢复局部顺序,最终实现整体的旋转。三次翻转的优势:每次翻转只涉及局部区域的改变,不需要额外的空间(空间复杂度 O(1)),并且翻转操作本身的时间复杂度是线性的(O(n)),因此这个算法具有很好的时间效率和空间效率。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n; // 处理k可能大于n的情况
if (k == 0) return; // 如果k是0,直接返回
// 翻转整个数组
reverse(nums.begin(), nums.end());
// 翻转前k个元素
reverse(nums.begin(), nums.begin() + k);
// 翻转剩余的元素
reverse(nums.begin() + k, nums.end());
}
};
- 时间复杂度:O(n)
-
空间复杂度:O(1)
思路三:环状替换
这段代码暂时先放在这里,暂时还没弄懂这么做的原理是什么
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
int count = gcd(k, n);
for (int start = 0; start < count; ++start) {
int current = start;
int prev = nums[start];
do {
int next = (current + k) % n;
swap(nums[next], prev);
current = next;
} while (start != current);
}
}
};
- 时间复杂度:O(n)
-
空间复杂度:O(1)

被折叠的 条评论
为什么被折叠?



