LeetCode 189 轮转数组|3种解法拆解,从暴力到O(1)原地最优解

文章前言

今天拆解 LeetCode Hot100 经典数组题:189. 轮转数组,题目要求将数组整体向右轮转 k 个位置,不仅可以练习数组基础操作,还能掌握「原地数组翻转」经典算法技巧,面试高频原题。本文依次讲解暴力移位、临时数组、三次数组翻转三种实现方案,对比时间、空间复杂度,带你吃透最优原地解法。

一、题目原题

题目描述

给定一个整数数组 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.尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。

2.你可以使用空间复杂度为 O(1) 的原地算法解决这个问题吗?

前置小知识点

当k > 数组长度时,旋转整数组长度次数等于数组还原,因此统一处理:k = k % nums.length,去除无效旋转。

二、解法一:暴力循环移位(直观易懂,大数据量超时)

思路

循环 k 次,每一轮只将数组最后一位元素取出,整体数组向后挪一位,最后把末尾元素放到数组头部。

Java 代码实现

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        // 循环k次,每次右移1位
        for (int i = 0; i < k; i++) {
            // 保存最后一个元素
            int last = nums[n - 1];
            // 数组整体后移一位
            for (int j = n - 1; j > 0; j--) {
                nums[j] = nums[j - 1];
            }
            // 头部赋值
            nums[0] = last;
        }
    }
}

复杂度分析

•时间复杂度:O(n×k),数组长度 n,循环 k 次移位,数组很长时直接超时

•空间复杂度:O(1),仅使用临时变量存储末尾值

优缺点

优点:逻辑直白,新手容易理解轮转逻辑;

缺点:效率极低,力扣大数据用例直接超时,工程基本不用。

三、解法二:临时数组辅助(空间换时间,刷题快速过题)

思路

新建一个等长临时数组,通过公式temp(i + k) % n = numsi计算每个元素旋转后的下标,全部存入临时数组,最后使用System.arraycopy将临时数组完整覆盖到原数组。

Java 代码实现

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        int[] temp = new int[n];
        // 填充临时数组
        for (int i = 0; i < n; i++) {
            temp[(i + k) % n] = nums[i];
        }
        // 将临时数组全部复制覆盖原数组
        System.arraycopy(temp, 0, nums, 0, n);
    }
}

关键知识点:System.arraycopy 数组覆盖

System.arraycopy(源数组, 源起始索引, 目标数组, 目标起始索引, 复制长度)

是 Java 底层原生数组拷贝方法,性能优于 for 循环赋值,可以直接原地修改原数组内容,适配本题void无返回值要求。

复杂度分析

•时间复杂度:O(n),仅一次遍历数组

•空间复杂度:O(n),额外开辟一个数组空间

优缺点

优点:代码简洁、一次遍历完成,通过率稳定;

缺点:不符合进阶原地算法要求,额外占用内存。

四、解法三:三次数组翻转(原地最优解,面试核心考点)

核心原理

无需额外数组,依靠自定义区间反转函数完成轮转,三步走(样例1,2,3,4,5,6,7,k=3演示)

  1. 整体数组反转:7,6,5,4,3,2,1
  2. 反转前 k 个元素:5,6,7,4,3,2,1
  3. 反转后半段剩余元素:5,6,7,1,2,3,4
    自定义 reverse 反转工具方法
    双指针实现区间原地反转,输入数组 + 左右边界,直接修改数组内容

Java 代码实现

反转函数代码

java 复制代码
// 闭区间[left,right]数组反转
private void reverse(int[] nums, int left, int right) {
    while (left < right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++;
        right--;
    }
}

完整代码

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int len = nums.length;
        k = k % len;
        reverse(nums, 0, len - 1);  // 整体反转
        reverse(nums, 0, k - 1);    // 前k位反转
        reverse(nums, k, len - 1);  // 后半段反转
    }

    // 区间反转工具函数
    private void reverse(int[] nums, int left, int right) {
        while (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
    }
}

复杂度分析

•时间复杂度:O(n),数组每个元素仅被交换 2 次

•空间复杂度:O(1),仅临时交换变量,纯原地操作

优缺点

优点:完美满足进阶 O (1 空间要求,面试标准答案;反转函数是数组通用工具,可以复用在大量算法题;

缺点:需要理解反转逻辑,新手需要画图辅助理解。

五、三种方案横向对比表

六、核心总结

1.数组旋转预处理必做k = k % nums.length,规避超大 k 值无效运算;
2.System.arraycopy是数组覆盖赋值高效 API,注意区分数组直接赋值(只是修改引用,无法修改原数组内容);
3.双指针reverse区间反转是数组高频工具函数,必须熟练手写;
4.面试遇到数组旋转类题目,直接给出三次反转原地解法,是最优解题答案。

相关推荐
MIngYaaa5201 小时前
2026暑期牛客多校3 2026-7-24
数据结构·算法·000
2501_937860941 小时前
Java 集合底层深度剖析:Map 与 Set、二叉搜索树、哈希表全解
java·数据结构·散列表
geats人山人海2 小时前
算法基础第二讲 基础算法下
算法
Hommy882 小时前
【剪映小助手】字符串列表转对象接口(Str List To Obds)
数据结构·list·剪映小助手·视频剪辑自动化·剪映api·开源剪映小助手
阳明山水2 小时前
销量预测2026下半年:新架构、新基准与新共识
人工智能·深度学习·算法·机器学习·架构
知识分享小能手2 小时前
高等数学学习教程,从入门到精通,定积分 — 知识点全面总结(9)
学习·算法·机器学习
noipp2 小时前
推荐题目:洛谷 P3726 [AHOI2017/HNOI2017] 抛硬币
c语言·数据结构·c++·算法·编程·洛谷·luogu
c238562 小时前
C/C++每日一练14
c语言·c++·算法
程序员-珍3 小时前
力扣 877. 石子游戏
算法·leetcode·游戏