除了三次翻转以外的O(1)空间思路:一步旋转到位

如果m=1,用一个萝卜一个坑的想法,萝卜A移到B,B到C,最后一个萝卜放到A的坑,就结束了。可是m可能是任意值,一次循环做完不一定所有萝卜的坑位都换了,比如数组1-6要旋转2个单位,就会分别存在1 -> 3 -> 5 -> 1和2 -> 4 -> 6 -> 2的两个环。
既然可能存在多个环,那么不是可以像DFS算岛的数量一样,遍历完一个环就去下一个环吗?在岛屿遍历中,我们可以mark走过的点,但这题无法添加mark,而且只有O(1)空间所以也不能用额外的数据结构记录访问过的点。但这些环有一个性质,就是若存在多个环,那么每个环的起点是相邻的 (假设以0作为起点,若相邻的1也在同一个环内的话,说明在走了n圈之后,可以达到1,那么以此类推,再走n圈就能到达2。只要继续走,所有点都能在该环内遍历到)。所以只要知道环的数量,就可以顺序访问找到下一个环的一个点开始遍历。
环的数量是多少呢?我们知道一个环里是不可能有重复点的,且所有环的长度相同,所以环的数量为数字总数n除以一个环的长度length。
长度就很好计算了,先遍历一个环我们就知道环长了。最后实现代码如下:
import java.util.*;


public class Solution {
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型一维数组 给定数组
     * @return int整型一维数组
     */
    public int[] solve (int n, int m, int[] a) {
        if ((m = m % n) == 0 || n < 2) {
            return a;
        }
        int circleLen = rotate(a, 0, m, n);
        int circleCount = n / circleLen;
        for (int i = 1; i < circleCount; i++) {
            rotate(a, i, m, n);
        }
        return a;
    }

    public int rotate(int[] a, int start, int m, int n) {
        int circleLen = 1;
        int cur = a[start];
        int next = start + m, nextVal;
        while (next != start) {
            circleLen++; // 统计环的长度+1
            nextVal = a[next]; // 把下一个数字B取出
            a[next] = cur; // 当前数字A占领B的位置
            cur = nextVal; // 拿着B
            next = (next + m) % n; // 找下一个位置C
        }
        a[start] = cur; // 最后剩下的数字放回环的起点
        return circleLen;
    }
}