导读:本期聚焦于小伙伴创作的《如何在 Java 中利用数组实现简单的循环移位算法并控制其空间复杂度》,敬请观看详情。把数组整体向左或向右挪几位,看似只是下标变动,却很容易写出额外开一份缓冲区的写法,让空间占用从常数级变成线性级。循环移位的核心在于不借助同等规模的辅助数组,仅用有限几个变量完成元素重排。常见思路有三次反转法与滚动交换法,前者通过逆序局部再逆序整体达成目的,后者用逐个暂存的方式把尾部元素顶到头部。理解这两种做法的交换次数与下标计算规则,才能在笔试与真实业务里既跑得对又省内存。

在 Java 里处理数组循环移位,本质是把下标按照某个偏移量重新映射。假设有一个长度为 n 的整型数组,希望每个元素向右循环移动 k 位,那么原来在位置 i 的元素应当去到位置 (i + k) % n。最直接的想法是新建一个同样长度的数组,把元素按新位置放好再拷贝回去,但这样空间复杂度是 O(n),当数组很大时会明显吃内存。真正合理的做法是在原数组上完成重排,只使用 O(1) 的额外空间。

如何在 Java 中利用数组实现简单的循环移位算法并控制其空间复杂度

三次反转法实现右移

三次反转是一种经典且容易证明正确性的办法。以右移 k 位为例,我们可以先把整个数组反转,再把前 k 个元素反转,最后把剩下的 n-k 个元素反转,就能得到正确结果。这种方法不需要任何额外数组,只用到一个交换两个元素的小函数,空间复杂度稳定为 O(1)。

从原理上看,反转操作相当于把序列头尾对调。第一次全反转让原本在尾部的 k 个元素跑到了最前面,但顺序是反的;后面两次局部反转分别把这两段恢复成正确顺序。它的时间复杂度是 O(n),每个元素被移动约两次,在绝大多数场景下都足够高效。

public class RotateArray {

    // 将数组从 start 到 end(含)的部分反转
    private static void reverse(int[] arr, int start, int end) {
        while (start < end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
            end--;
        }
    }

    // 向右循环移位 k 位,空间复杂度 O(1)
    public static void rotateRight(int[] arr, int k) {
        int n = arr.length;
        if (n == 0) {
            return;
        }
        // 防止 k 大于数组长度,取模简化
        k = k % n;
        if (k == 0) {
            return;
        }
        reverse(arr, 0, n - 1);
        reverse(arr, 0, k - 1);
        reverse(arr, k, n - 1);
    }

    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4, 5};
        rotateRight(data, 2);
        for (int v : data) {
            System.out.print(v + " ");
        }
        // 输出:4 5 1 2 3
    }
}

上面的代码里,reverse 方法只用到 temp 这一个临时变量,因此无论数组多大,额外空间都不会随规模增长。需要注意的是,传入的 k 应当先做取模处理,否则当 k 大于 n 时会出现无效反转区间。对于左移,只需把 k 换成 n - (k % n) 再调用同一套逻辑即可。

滚动暂存法控制空间

如果不想做三次反转,也可以用滚动暂存的方式:每次把最后一个元素取出来,整体向右挪一格,再把暂存值放到开头,重复 k 次。这种做法思路直观,同样只用了常数个变量,但时间复杂度在极端情况下会达到 O(n * k),当 k 接近 n 时比三次反转慢不少。

为了缓解重复移动的问题,可以改为按环移动:计算最大公约数 gcd(n, k),把数组分成若干条独立循环链,每条链上直接跳步赋值。这样每个元素只被写一次,时间复杂度回到 O(n),空间依然是 O(1)。下面给出基于最大公约数的写法。

public class RotateByGcd {

    private static int gcd(int a, int b) {
        while (b != 0) {
            int t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    // 向右循环移位 k 位,按环跳跃,空间 O(1)
    public static void rotate(int[] arr, int k) {
        int n = arr.length;
        if (n == 0) {
            return;
        }
        k = k % n;
        if (k == 0) {
            return;
        }
        int cycles = gcd(n, k);
        for (int start = 0; start < cycles; start++) {
            int current = start;
            int prev = arr[start];
            do {
                int next = (current + k) % n;
                int temp = arr[next];
                arr[next] = prev;
                prev = temp;
                current = next;
            } while (current != start);
        }
    }

    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4, 5, 6, 7};
        rotate(data, 3);
        for (int v : data) {
            System.out.print(v + " ");
        }
        // 输出:5 6 7 1 2 3 4
    }
}

这段代码中,cycles 表示独立循环链的数量。每条链从 start 出发,沿着加 k 取模的路径走,直到回到原点。prev 保存上一次被挤掉的值,整个过程中只借助了 prev、temp 等固定数量的变量。相比简单滚动,它避免了同一个元素被反复搬动,在 k 较大时优势明显。

方法对比与选用建议

从工程角度看,三次反转法代码最短、最不容易写错,适合面试快速实现以及对性能要求不极端的业务。按环跳跃法在理论时间上更优,但下标推算稍复杂,维护时可读性略差。二者空间复杂度都是 O(1),都不会因为数组变大而多申请内存。

如果数组元素类型是重量级对象,反转法和跳跃法都只交换引用而不发生拷贝,依然安全。需要留意的是,Java 里数组是定长结构,上述算法都直接修改原数组;若调用方不允许改动入参,就只能在方法内新建一个长度相同的数组做中转,那样空间复杂度会变为 O(n),这一点要在接口设计时就讲清楚。

方法时间复杂度空间复杂度实现难度
三次反转O(n)O(1)
简单滚动O(n*k)O(1)
按环跳跃O(n)O(1)

总结来说,在 Java 中控制数组循环移位的空间占用,核心就是拒绝再分配一个同等规模的数组。掌握反转与跳跃两种原地算法,就能在内存敏感或数据量大的场景下写出更稳的代码。

Java数组循环移位空间复杂度修改时间:2026-08-02 15:51:29

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。