题目的大意是将一个长度为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;
}
很明显,需要优化空间的使用。
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);
}
不过这种方法需要对每个位置写入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);
}
}