问题描述
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。要求使用空间复杂度为 O(1) 的原地算法。
示例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]
解题思路
这是一道比较常见的题,很多教材的课后习题中也有这道题目,第一次遇见这道题时我也是采用暴力算法,循环求解,看到翻转解法时很不能理解,不知道为什么别人能想出这样的算法,现在感觉多少有些了解了,尝试讲一下。
数组右移k个位置,我们只看最后结果,就是数组最后的k位平移到了数组最前面的k位。数组最前面的(length-k)位向后平移k个位置。按照这样的想法,我们先将前面的(length-k)翻转,为最后一次翻转产生的逆序做提前的翻转,使之翻转后仍是原有顺序,第二个翻转也是这个道理。如果这样看的话,我们先执行一次全体翻转,在执行1,2步翻转也是一样的,只不过是翻转的起始位置发生了变化。翻转本省就带有位置的移动,这是十分重要的一点。
代码实现
public class TranslationArray {
public static void main(String[] agrs){
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
translationArray(nums, 6);
for (int num : nums) {
System.out.println(num);
}
}
public static void translationArray(int[] nums, int k){
// 当k大于数组的长度时,避免产生数组下标越界
k %= nums.length;
// 当位移为空或者数组长度小于2时,不进行操作
if(k == 0 || nums.length <= 1) return;
// 翻转前(length - k)位
reverse(nums, 0, nums.length - 1 - k);
// 翻转后k位
reverse(nums, nums.length - k, nums.length -1);
// 翻转整个数组
reverse(nums, 0, nums.length -1);
}
public static void reverse(int[] array, int start, int end){
int temp, mid = (end - start) / 2;
// 依次交换首位位置元素,向中间循环
for(int i = 0; i <= mid; i++){
temp = array[start + i];
array[start + i] = array[end - i];
array[end - i] = temp;
}
}
}