力扣hot100:轮转数组(常规思路与三步反转讲解)(189)
轮转数组问题要求将数组元素向右移动 k
个位置,尾部元素移动到数组头部。本文讲解一种时间复杂度 O(n)、空间复杂度 O(1) 的优雅解法——三步反转算法。
问题描述
常见误区
正常思路
class Solution {public void rotate(int[] nums, int k) {int n = nums.length;int[] newArr = new int[n];for (int i = 0; i < n; ++i) {newArr[(i + k) % n] = nums[i];}System.arraycopy(newArr, 0, nums, 0, n);}
}
三步反转算法(最优解)
核心思想
通过三次反转实现轮转:
- 整体反转:将整个数组倒序排列。
- 反转前
k
个元素:恢复轮转后应位于数组头部元素的顺序。 - 反转剩余元素:恢复轮转后位于数组尾部的元素的顺序。
图解过程(以 nums = [1,2,3,4,5,6,7]
, k=3
为例)
步骤 | 操作 | 结果 |
---|---|---|
原始数组 | - | [1,2,3,4,5,6,7] |
整体反转(0 → n-1 ) | 首尾交换 | [7,6,5,4,3,2,1] |
反转前 k 个(0 → k-1 ) | 恢复头部顺序 | [5,6,7,4,3,2,1] |
反转剩余部分(k → n-1 ) | 恢复尾部顺序 | [5,6,7,1,2,3,4] |
️ 关键细节:处理 k
过大
若 k
超过数组长度,需取模:k = k % nums.length
。 原因:轮转 n
次后数组恢复原状,有效轮转次数为 k mod n
。
代码实现
class Solution {public void rotate(int[] nums, int k) {k=k%nums.length;swap1(nums,0,nums.length-1);swap1(nums,0,k-1);swap1(nums,k, nums.length-1);}private void swap1(int[] nums, int i, int length) {while(i<length){int temp=nums[i];nums[i]=nums[length];nums[length]=temp;i++;length--;}}
}
⏱ 复杂度分析
步骤 | 时间复杂度 | 空间复杂度 |
---|---|---|
整体反转 | O(n) | O(1) |
前 k 个反转 | O(k) | O(1) |
剩余部分反转 | O(n-k) | O(1) |
总计 | O(n) | O(1) |
对比额外空间解法(使用新数组): 时间复杂度 O(n),但空间复杂度 O(n)。
三步反转法是空间最优解。
总结
三步反转法的核心在于逆向思维:
- 整体反转打破原有顺序;
- 分段反转精准定位轮转后的子数组位置。 此方法高效且代码简洁,是解决数组轮转问题的首选方案。掌握后,类似旋转问题(如字符串旋转)均可触类旁通 。