1. 问题描述
本文所述问题是指将一个长度为n的数组中的元素向右移动k个位置,其中k是非负整数。例如,给定数组 nums = [1,2,3,4,5,6,7]
和 k = 3
,将数组向右移动3个位置后得到 [5,6,7,1,2,3,4]
。
2. 解法分析
2.1 暴力法
暴力法是最直观的方法之一,通过多次交换相邻元素的位置来实现循环移位。具体的方法是,将数组中的最后一个元素与倒数第二个元素交换位置,然后将倒数第二个元素与倒数第三个元素交换位置,以此类推,直到将第一个元素与最后一个元素交换k次,即可实现循环移位。时间复杂度为 O(kn)
。
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
for (int i = 0; i < k; i++) {
int temp = nums[n - 1];
for (int j = n - 1; j > 0; j--) {
nums[j] = nums[j - 1];
}
nums[0] = temp;
}
}
2.2 环状替换
环状替换是一种更加高效的方法,我们观察移动后的数组可以发现,每个元素都是移动的k个位置以后才到达终点的,而且多个元素可能会到达同一个位置,因此可以将这些元素看作是一个循环的"环",并将每个元素在这个"环"上移动,从而实现整个数组的循环移位。具体的做法是,从第一个元素开始,将其向右移动k个位置后到达的位置上的元素保存下来,并将该位置上的元素向右移动k个位置,直到回到第一个元素,这样一个"环"就完成了移动。然后再从未移动的下一个位置开始,重复上述过程,直到所有元素都移动完毕。时间复杂度为 O(n)
。
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
int count = 0;
for (int start = 0; count < n; start++) {
int current = start;
int prev = nums[start];
do {
int next = (current + k) % n;
int temp = nums[next];
nums[next] = prev;
prev = temp;
current = next;
count++;
} while (start != current);
}
}
2.3 数组翻转
数组翻转是一种更加简单的方法,通过反转数组中的前k个元素,后n-k个元素,以及整个数组,即可实现循环移位。具体的做法是,先将前k个元素翻转,再将后n-k个元素翻转,最后将整个数组翻转。时间复杂度为 O(n)
。
void reverse(vector<int>& nums, int left, int right) {
while (left < right) {
swap(nums[left], nums[right]);
left++;
right--;
}
}
void rotate(vector<int>& nums, int k) {
int n = nums.size();
k = k % n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
3. 总结
本文介绍了三种不同的方法来实现循环移位,包括暴力法、环状替换和数组翻转。这三种方法的时间复杂度分别为 O(kn)
、O(n)
和O(n)
,其中环状替换和数组翻转的时间复杂度较低,因此推荐使用这两种方法来实现循环移位。同时,需要注意数组下标的边界问题以及循环次数的计算方法,以避免出现错误。