难度:简单
1. 题目描述
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 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]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的原地算法。
2. 解题思路
英文版的有答案:https://leetcode.com/problems/rotate-array/solution/
这里记录一下第4种方法的理解:Approach #4 Using Reverse [Accepted]
假设对长度为 n 的数组,进行 k 次旋转。
首先要找到最终有效的次数,因为旋转的循环的,当对数组进行 n 次旋转时,相当于没有对数组进行操作,同时该方法必须先找到最终有效次数,否则没法对数组进行镜像操作。
为什么在不同位置采用三次镜像操作就能实现数组的 k 次旋转,连同答案和自己的理解整理为如下:
-
先计算 的有效次数,因为旋转是循环进行的,像自行车链条一样,当元素旋转一整圈即: 时,所有元素回到了原来的位置,所以 次旋转的有效次数为 。
-
我们将数组以 为边界拆分成两部分看待,前 个元素部分: 和 后 个元素部分 ,为什么以 为分界点呢?因为对数组进行 次旋转后, 和 将互换位置。
如数组:,长度 ,进行 次旋转,对应 ,,旋转结束后得到数组: , -
为了实现将 和 位置互换,这里采用将整个数组镜像的策略。
原元素:
镜像后: -
对数组进行镜像操作后,虽然 和 位置互换,但是其内部元素也被镜像了,为了将 和 内部元素的顺序恢复,对 和 再分别进行一次镜像。
举例如下原数组:
镜像前: ,
镜像后: ,
对 和 分别进行一次镜像后得到的最终结果: ,
最终答案:算法思想再精简一点如下:
- 对数组以 划分成两部分,分为前 个元素部分,和后 个部分,这样划分的目的是旋转 次后这两部分正好位置互换。
- 为了实现这种位置互换需要将数组镜像
- 镜像虽然将两部分数据整体的位置转换了过来,但是两部分数组内部数据顺序错误,需要对两部分数组分别镜像
Java实现:
class Solution { public void rotate(int[] nums, int k) { if (nums == null || nums.length < 2) { return; } // 计算有效旋转次数 k %= nums.length; // 对整个数组镜像,使 S2 和 S1 位置互换 reverse(nums, 0, nums.length - 1); // 对 S2 镜像处理,使其内部元素顺序恢复 reverse(nums, 0, k - 1); // 对 S1 镜像处理,使其内部元素顺序恢复 reverse(nums, k, nums.length - 1); } private void reverse(int[] nums, int start, int end) { while (start < end) { int temp = nums[end]; nums[end] = nums[start]; nums[start] = temp; start++; end--; } } }
文章来源: https://blog.csdn.net/wangxiaoan1234/article/details/90740819