源本科技 | 码上会

数组循环右移

2026/02/11
16
0

题目

nn 个整数,使其前面各数顺序向后移mm 个位置,最后mm 个数变成最前面的mm 个数。

例如:数组[1,2,3,4,5][1, 2, 3, 4, 5] 向后移m=2m=2 位,结果应为[4,5,1,2,3][4, 5, 1, 2, 3]

说明

本题要求实现数组的循环右移(也称“旋转”)操作。关键在于理解“向后移mm 个位置”的含义:

  • 原数组中最后mm 个元素移到最前面

  • 剩下的前nmn - m 个元素整体向后平移mm

这实际上是将数组分为两部分:

  • 后段A[nm:n]A[n-m : n]

  • 前段A[0:nm]A[0 : n-m]

然后将后段拼接到前段之前,形成新数组。

注意:mnm \geq n,应取m=mmodnm = m \bmod n,避免无效移动(如移nn 位等于没动)。题目示例未处理此情况,但实际应用中建议加上。

运行示例

输入

输入数字个数n:
5
输入后移位数m:
2
请输入第1个数: 1
请输入第2个数: 2
请输入第3个数: 3
请输入第4个数: 4
请输入第5个数: 5

输出

原数据排序为:
1 2 3 4 5 
移动后排序为;
4 5 1 2 3 

👈点击左箭头查看答案(一定要在自己思考并实现后再看参考答案哦!)

规律分析

设原数组为A[0..n1]A[0..n-1],目标是得到新数组BB,满足:

B[i]={A[nm+i],0i<mA[im],mi<nB[i] = \begin{cases} A[n - m + i], & 0 \leq i < m \\ A[i - m], & m \leq i < n \end{cases}

等价于:

B=A[nm:n]+A[0:nm]B = A[n-m:n] + A[0:n-m]

切片拼接操作。

此操作在计算机科学中称为 “数组旋转”,常见于缓存管理、密码学、图像处理等领域。高效实现方式包括:

  • 使用额外空间(如本题两种方法)

  • 原地旋转(三次反转法,空间复杂度O(1)O(1)

程序实现

方法一

使用 LinkedList 和子列表(SubList)

import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
​
public class Demo {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("输入数字个数n:");
        int n = in.nextInt();
        System.out.println("输入后移位数m:");
        int m = in.nextInt();
        
        // 处理 m >= n 的情况(健壮性增强)
        m = m % n;
        if (m == 0) {
            System.out.println("无需移动");
            return;
        }
​
        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            System.out.println("请输入第" + (i + 1) + "个数:");
            list.add(in.nextInt());
        }
        
        System.out.println("原数据排序为:");
        for (int t : list) {
            System.out.print(t + " ");
        }
        System.out.println();
​
        // 切分:后 m 个 + 前 n-m 个
        List<Integer> lastM = list.subList(list.size() - m, list.size());
        List<Integer> firstPart = list.subList(0, list.size() - m);
        
        // 将后 m 个插入到前段开头
        firstPart.addAll(0, lastM);
​
        System.out.println("移动后排序为:");
        for (int t : firstPart) {
            System.out.print(t + " ");
        }
    }
}

注意:subList 返回的是原列表的视图,直接修改会影响原列表。此处通过 addAll(0, ...) 修改 firstPart(即原 list 的前段),从而改变整个 list


方法二

使用辅助数组(更清晰)

import java.util.Scanner;
​
public class Demo {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("请定义数组的长度:");
        int n = in.nextInt();
        System.out.println("请输入移动的位数:");
        int m = in.nextInt();
        
        // 增强健壮性
        m = m % n;
        if (m == 0) {
            System.out.println("无需移动");
            return;
        }
​
        int[] arr = new int[n];
        int[] temp = new int[m]; // 临时保存最后 m 个数
​
        // 输入
        for (int i = 0; i < n; i++) {
            System.out.println("请输入第" + (i + 1) + "个数:");
            arr[i] = in.nextInt();
        }
​
        System.out.println("排序前:");
        for (int x : arr) System.out.print(x + " ");
        System.out.println();
​
        // 1. 保存最后 m 个数到 temp
        for (int i = 0; i < m; i++) {
            temp[i] = arr[n - m + i];
        }
​
        // 2. 将前 n-m 个数向后移动 m 位(从后往前避免覆盖)
        for (int i = n - m - 1; i >= 0; i--) {
            arr[i + m] = arr[i];
        }
​
        // 3. 将 temp 中的数放到最前面
        for (int i = 0; i < m; i++) {
            arr[i] = temp[i];
        }
​
        System.out.println("排序后:");
        for (int x : arr) System.out.print(x + " ");
    }
}

补充知识

  • 经典算法:该问题也可用 三次反转法 实现原地旋转(不使用额外数组):

    reverse(arr, 0, n-1);
    reverse(arr, 0, m-1);
    reverse(arr, m, n-1);

    时间复杂度O(n)O(n),空间复杂度O(1)O(1)

  • 应用场景:循环缓冲区、字符串旋转、加密算法中的位移操作等。

此题是理解数组切片、索引映射和内存操作的重要练习,也是面试常见题型。