数组的循环右移

题目的大意是将一个长度为size的数组A内的元素循环右移n位(当然左移也可以),比如数组 {1, 2, 3, 4, 5}右移3位之后就变成{3, 4, 5, 1, 2}。
1. 这题最简单的做法是开另一个大小一样的数组B,遍历一下,令B[(i+n) % size] = A[i],再将B的内容写回到A即可。这个方法的时间复杂度为O(N),空间复杂度也为O(N)。

public int[] moveData(int[] data, int n)
    {
        int size = data.length;
        int data2[] = new int[size];
        for (int i=0; i < size; i++)
        {
            data2[(i+n)%size] = data[i];
        }
        return data2;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

很明显,需要优化空间的使用。
2. 翻转的思想,例如abcdefgh 前移3位,就先反转前3位得到cbadefgh, 再反转后5位得到cbahgfed ,再全部反转一遍 defghabc,对于12345右移2位,可以先反转后2位是12354,然后反转前3位是32154,再全部反转得到45123。显然,这样不用开辟新空间。

//数组元素从m到n反转 
public void Reverse(int m, int n, int[] a)
    {
        for(int i = 0; i < (Math.abs(n-m)/2)+1; i++)
        {
            int temp = a[n-1-i];
            a[n-1-i] = a[m-1+i]  ;
            a[m-1+i] = temp;
        }

    }
  public void ShiftRight(int step, int[] a) {
        int size = a.length;
        int m = size -step;
        if(m<=0) return;
        Reverse2(m +1, size, a);
        Reverse2(1, m, a);
        Reverse2(1, size, a);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

不过这种方法需要对每个位置写入2次,看上去也不怎么好,那有没有更好的呢?
3. 我们要做的只是把每个元素放到它应该在的位置,比如开头的例子,1应该放在4的位置,把1放好之后,4就没地方了,那4应该在哪呢,在2的位置,以此类推,就可以把所有的元素都放好,而且只放了一次。看上去这样做很完美,但仔细想想就能想出反例子,比如{1, 2, 3, 4, 5, 6, 7, 8, 9}右移3位,就是1放在4个位置,4放在7的位置,然后7放回1,这时候一圈兜完了,但只排好了3个元素,剩下的6个元素没有动过,怎么办呢?继续下个呗,就是2,然后2、5、8也排好了,继续3、6、9,这时候下一个元素是1了(因为1之前就被放在了4的位置),应该停止了,那程序怎么会知道停在这里了,于是就想到了最大公约数,9和3的最大公约数是3,于是做前3个数的循环就可以了,为什么上一个例子只需做一次,因为元素个数(5)和移动位数(3)互质。

//求最大公约数
public int gcd(int a, int b) {
        if (a == 0 || b == 0) {
            return a + b;
        }
        int t;
        while(b != 0) {
            t = a % b;
            a = b;
            b = t;
        }

        return a;
    }

  public void ShiftRight2(int n, int size, int[] a) {
        n %= size;
        if (n == 0) return;
        for (int i = 0, _i = gcd(size, n); i < _i; ++i) {
            int k = i;
            int t = a[k];
            do {
                k = (k + n) % size;
                int tt = a[k];
                a[k] = t;
                t = tt;
            } while(k != i);
        }
    }
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值