LeetCode hot100:189 轮转数组:三种解法从入门到精通
问题描述:
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [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:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
解决方法:
方法一:使用额外数组
算法思路:
- 创建一个与原数组相同大小的新数组;
 - 将原数组后 k 个元素复制到新数组前 k 个位置;
 - 将原数组前 n - k 个元素复制到新数组的后 n - k 个位置;
 - 将新数组内容复制回原数组。
 
def rotate(nums, k):n = len(nums)k = k % n  # 关键步骤:处理 k 大于数组长度的情况# 创建新数组rotated = [0] * n# 将后k个元素放到新数组的前面for i in range(k):rotated[i] = nums[n - k + i]# 将前n-k个元素放到新数组的后面for i in range(n - k):rotated[k + i] = nums[i]# 将结果复制回原数组for i in range(n):nums[i] = rotated[i]复杂度分析:
- 时间复杂度:O(n)
 - 空间复杂度:O(n)
 
优点:思路直观,易于理解
缺点:需要开辟额外空间
方法二:三次反转法(最佳)
算法思路:
- 反转整个数组:
[1,2,3,4,5,6,7]->[7,6,5,4,3,2,1] 反转前k个元素:[7,6,5,4,3,2,1]->[5,6,7,4,3,2,1]- 反转剩余元素:
[5,6,7,4,3,2,1]->[5,6,7,1,2,3,4] 
class Solution:def rotate(self, nums: List[int], k: int) -> None:n = len(nums)k = k%n #防止数组越界def reverse(start,end):while start < end:nums[start],nums[end] = nums[end],nums[start]start += 1end -= 1reverse(0,n-1)# 第一步:反转整个数组reverse(0,k-1)# 第二步:反转前k个元素reverse(k,n-1)# 第三步:反转剩余元素复杂度分析:
- 时间复杂度:O(n)
 - 空间复杂度:O(1)
 
优点:原地操作,空间利用率高
缺点:思路抽象
方法三:环状替换
算法思路:
- 从位置0开始,将元素放到正确位置;
 - 记录被替换的元素,继续处理下一个位置;
 - 如果回到起点,说明完成了一个环的替换;
 - 从下一个位置开始新的环,直到所有元素都处理完毕。
 
def rotate(nums, k):n = len(nums)k = k % ncount = 0  # 记录已经移动的元素数量start = 0while count < n:current = startprev = nums[start]while True:next_idx = (current + k) % nnums[next_idx], prev = prev, nums[next_idx]current = next_idxcount += 1if start == current:  # 回到起点,结束当前环breakstart += 1  # 处理下一个环复杂度分析:
- 时间复杂度:O(n)
 - 空间复杂度:O(1)
 
优点:原地操作,空间效率高
缺点:思路抽象,实现复杂
问题详解:
上述三个方法都提到了 k = k % n ,这个操作很关键,原因如下:
- 防止数组越界:当 k > n 时,避免访问非法内存;
 - 优化性能:避免不必要的重复旋转;
 - 正确性:旋转n次等于没旋转,旋转n+1次等于旋转1次。
 
示例:nums = [1,2,3,4],k = 6
k % n = 6 % 4 = 2  # 实际只需要旋转2次
总结:
通过这个问题可以学习到:
多种解题思路:同一个问题可以有多种不同的解决方法;
空间复杂度优化:如何在不使用额外空间的情况下解决问题;
数学思维:利用模运算和反转操作简化问题;
边界条件处理:考虑k大于数组长度等特殊情况。
推荐使用三次反转法,因为它在时间复杂度和空间复杂度之间取得了很好的平衡,代码简洁且效率高。
