数组循环右移

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_21595363/article/details/50789845

描述:将一个长度为 n 的数组 A 的元素循环右移(ROR, Rotate Right)k 位,比如数组 1, 2, 3, 4, 5 循环右移 3 位之后就变成 3, 4, 5, 1, 2。
要求:只能用一个元素大小的附加存储,元素移动或交换次数为O(n)。

开始时自己想的算法就是最简单原始的一种:
每次将数组右移一位,循环k次:

void function(int *A,int n,int k){
    int tmp=A[n-1];
    for(int i=0;i<k;i++){
       for(int j=1;j<=n;j++){
         A[j]=A[j-1];
       }
       A[0]=tmp;
    }
}

但这个方法的时间复杂度是O(n*k),不符合题目要求,有没有更好的算法呢?

google了发现实现这个的是经典的“三步反转法”:
记 A 的前 n-k 位为 X,后 k 位为 Y,则 A=XY,将 A 循环右移 k 位后,应该得到YX。根据该算法,先将 A 整体倒置,得到 (XY )^T = Y^T • X^T,然后将前 k 位倒置,得到 Y • X^T,最后将后n-k 位倒置,得到 YX,正好是所求的结果。
这样我们可以将数组分为两段,先将它们全部倒置,再将前k位倒置,再将后n-k位倒置,即 1, 2, 3, 4, 5 变成 5, 4, 3, 2, 1,然后将前 k 位倒置,即 3, 4, 5, 2, 1,再将后n-k 位倒置,即 3, 4, 5, 1, 2,完成。

static void swap(int array[], int i, int j) {
    const int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

static void reverse(int array[], int begin, int end) { 
    end--;
    while (begin < end)
        swap(array, begin++, end--);
}

void ror3(int *array, int n, int k) {
    k %= n;
    if (k == 0)
        return;

    reverse(array, 0, n);
    reverse(array, 0, k);
    reverse(array, k, n - k);
}
展开阅读全文

没有更多推荐了,返回首页