C语言实现数组循环左移/右移

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;
    }
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值