[原题链接](初级算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台 (leetcode-cn.com))
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题
- 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
三种解法
public class Solution {
/**
* 旋转数组(法一)
* 思路:k取模后,将后k个元素暂存,然后将前length - k个元素复制到新的位置,
* 最后填补前k个位置
* 空间复杂度O(n),时间复杂度O(2n)
* @param nums
* @param k
*/
public void rotate1(int[] nums, int k) {
//取模,避免越界
k = k % nums.length;
int[] temp = new int[k];
//暂存后k个元素
for (int i = 0; i < k; i++) {
temp[k - 1 - i] = nums[nums.length - 1 - i];
}
//将前length - k个元素复制到新的位置
for (int i = nums.length - 1 - k; i >= 0; i--) {
nums[i + k] = nums[i];
}
//填补前k个位置
for (int i = 0; i < k; i++) {
nums[i] = temp[i];
}
}
/**
* 法二:
* 思路:经观察,旋转一次(原地)很容易做到,不妨循环执行k次旋转操作
* 空间复杂度O(1),时间复杂度O(n^2)超时
* @param nums
* @param k
*/
public void rotate2(int[] nums, int k){
//取模,减少循环次数
k = k % nums.length;
if (k == 0 || nums.length == 1) return;
for (int i = 0; i < k; i++) {
rotateOnce(nums);
}
}
private void rotateOnce(int[] nums){
if(nums.length == 1) return;
int temp = nums[nums.length - 1];
//亦步亦趋
for (int i = nums.length - 1; i > 0; i--) {
nums[i] = nums[i - 1];
}
nums[0] = temp;
}
/**
* 法三:
* 思路:翻转,先翻转整个数组,这样“两个整体的顺序”是正确的,不过局部是逆序的
* 然后翻转前k个元素,使得前k个元素顺序正确;翻转后k - 1个元素,使得后k - 1个元素顺序正确
* 空间复杂度O(1),时间复杂度O(n)
* @param nums
* @param k
*/
public void rotate3(int[] nums, int k){
k = k % nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
/**
* 翻转数组
* 参数:start起始位置, end结束位置
* @param nums
* @param start
* @param end
*/
private void reverse(int[] nums, int start, int end) {
int temp = 0;
while(end > start){
temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
end--;
start++;
}
}
}
不难看出,比起法一和法二,法三无论是空间上(O(1))还是时间上(O(n))都更优。