【力扣-189. 旋转数组 ✨】Python笔记

0 阅读3分钟

一、前置核心知识点

1. 数组轮转基础概念

  • 定义:将数组的每个元素向右(或向左)移动 k 个位置,末尾元素循环回到头部。
  • 示例[1,2,3,4,5,6,7] 右移 3 位 → [5,6,7,1,2,3,4]
  • 关键点k 可能大于数组长度 n,需先取模 k = k % len(nums) 简化操作。

2. 三次反转法(最优解法)

  • 核心思想:利用局部反转 + 整体反转,无需额外空间,实现 O (1) 空间复杂度。

  • 操作步骤

    1. 反转前 n-k 个元素
    2. 反转后 k 个元素
    3. 反转整个数组
  • 原理:相对位移不变,通过三次反转实现整体右移效果。

3. Python 列表反转技巧

  • 切片反转nums[:] = nums[::-1](原地反转)
  • 双指针反转:自定义 reverse 函数,实现区间 [i, j] 的反转,效率更高且可控。

二、经典算法题:轮转数组(LeetCode 189)

题目描述

给定一个整数数组 nums,将数组中的元素向右移动 k 个位置,其中 k 是非负数。要求原地修改数组,不使用额外空间。

示例

  • 输入nums = [1, 2, 3, 4, 5, 6, 7]k = 3
  • 输出[5, 6, 7, 1, 2, 3, 4]

最优解法:三次反转法

核心思路

  1. 处理 k 值:如果 k >= len(nums),通过取模减少移动次数。
  2. 局部反转:反转前 n-k 个元素,反转后 k 个元素。
  3. 整体反转:反转整个数组,得到最终结果。

代码实现

from typing import List
class Solution: 
    def rotate(self, nums: List[int], k: int) -> None: 
    """ Do not return anything, modify nums in-place instead. """
    
        n = len(nums) 
        k = k % n # 取模,防止k大于数组长度
        
        if k == 0:
            return  # 无需反转
        
        # 定义反转函数:双指针实现 [i, j] 区间反转 
        def reverse(i: int, j: int):
            while i < j: 
                nums[i], nums[j] = nums[j], nums[i] 
                i += 1 
                j -= 1 
                
        # 1. 反转前 n-k 个元素 
        reverse(0, n - k - 1)
        # 2. 反转后 k 个元素 
        reverse(n - k, n - 1)
        # 3. 反转整个数组 
        reverse(0, n - 1)

三、关键逻辑与执行流程

1. 算法执行流程演示

nums = [1,2,3,4,5,6,7]k=3n=7 为例:

  1. 初始[1, 2, 3, 4, 5, 6, 7]
  2. 步骤 1(反转前 4 个)[4, 3, 2, 1, 5, 6, 7]
  3. 步骤 2(反转后 3 个)[4, 3, 2, 1, 7, 6, 5]
  4. 步骤 3(反转整体)[5, 6, 7, 1, 2, 3, 4] ✅ 结果达成

2. 为什么要取模?

  • 例如数组长度 n=5k=7
  • 移动 7 位等价于移动 7 % 5 = 2 位,减少无效操作。

3. 复杂度分析

  • 时间复杂度:O (n),每个元素被反转操作访问两次。
  • 空间复杂度:O (1),仅使用常数额外空间,完美符合题目要求。

四、算法对比与拓展

1. 常见解法对比

方法时间复杂度空间复杂度评价
三次反转法O(n)O(1)✅ 最优,推荐使用
切片法O(n)O(n)代码简洁,但违背原地修改要求
循环替换O(n)O(1)逻辑复杂,容易处理循环引用

2. 拓展应用

  • 左旋转:调整三次反转的顺序,即可实现向左旋转。
  • 部分旋转:只旋转数组指定区间,修改 reverse 的参数范围即可。