1. 最简单也是最笨的方法:每次移动一位,循环移动 k%n 次。需要移位(k%n)*n次。
// n是数组元素个数,k是循环移动位数
void leftMove(int A[], int n, int k)
{
for(int i = 0; i < k % n; i++){
int x = A[0];
for(int j = 0; j < n - 1; j++){
A[j] = A[j + 1];
}
A[n - 1] = x;
}
}
2. 常见的方法:翻转3次。需要移位2n次。
假设现在数组大小n = 8,移动次数k = 3, 向👉移动。||是分隔符。
初始:0 1 2 3 4 || 5 6 7
翻转(左):4 3 2 1 0 || 5 6 7
翻转(右):4 3 2 1 0 || 7 6 5
翻转(全):5 6 7 0 1 2 3 4
如果是向👈移动,那么
初始:0 1 2 || 3 4 5 6 7
翻转(左):2 1 0 || 3 4 5 6 7
翻转(右):2 1 0 || 7 6 5 4 3
翻转(全):3 4 5 6 7 0 1 2
void resverse(int A[], int from, int to)
{
int mid = (from + to + 1) / 2;
for(int i = from; i < mid; i++){
int t = A[i];
A[i] = A[to - i + from];
A[to - i + from] = t;
}
}
// n是数组元素个数,k是循环移动位数
void leftMove(int A[], int n, int k)
{
int q = k % n;
resverse(A, 0, q - 1);
resverse(A, q, n - 1);
resverse(A, 0, n - 1);
}
// n是数组元素个数,k是循环移动位数
void rightMove(int A[], int n, int k)
{
int q = k % n;
resverse(A, 0, n - q - 1);
resverse(A, n - q, n - 1);
resverse(A, 0, n - 1);
}
3. 最优算法,移位n次。
假设现在数组大小n = 8,初始:0 1 2 3 4 5 6 7。
移动次数k = 3, 向👉移动。
0 > 3 > 6 > 1 > 4 > 7 > 2 > 5 > 0,这是1个闭循环。
若令k = 2,那么
0 > 2 > 4 > 6 > 0
1 > 3 > 5 > 7 > 1,这是2个闭循环。
若令k = 4,那么
0 > 4 > 0
1 > 5 > 1
2 > 6 > 2
3 > 7 > 3,这是4个闭循环。
其实,闭循环的个数就是n和k的最大公因数d,而每个闭循环的大小就是n/d,需要n/d次移位。总共需要n/d*d=n次移位。
// 实现求最大公因数。前提是a > 0 , b > 0
int gcd(int a, int b)
{
if(b == 0) return a;
return gcd(b, a % b);
}
下标6向👉移动3位到下标1的位置:mod((6+3), 8)=1
下标0向👈移动3位到下标5的位置:mod((0-3), 8)=5
因此需要写一个求余函数。对于计算机而言,(-3)%8=-(3%8)=-3,但其实正确答案应该是5。所以当被除数小于零时,结果需要加上除数。
// 实现求余。要求除数b > 0
int mod(int a, int b)
{
return a < 0 ? b + a % b : a % b;
}
下面分别是👈移和👉移。不过循环移动次数可以为负值。
// n是数组元素个数,k是循环移动位数
// 循环左移,k > 0 左移,k < 0 右移
void leftMove(int A[], int n, int k)
{
int d = gcd(n, abs(k));
for(int i = 0; i < d; i++){ // d个闭循环
int t = i;
int x = A[t];
for(int j = 1; j < n / d; j++){ // n/d-1次移位
A[t] = A[mod(t + k, n)];
t = mod(t + k, n);
}
A[t] = x; // 第n/d次移位
}
}
// n是数组元素个数,k是循环移动位数
// 循环右移,k > 0 右移,k < 0 左移
void rightMove(int A[], int n, int k)
{
int d = gcd(n, abs(k));
for(int i = 0; i < d; i++){
int t = i;
int x = A[t];
for(int j = 1; j < n / d; j++){
A[t] = A[mod(t - k, n)];
t = mod(t - k, n);
}
A[t] = x;
}
}