n元一维向量旋转问题

问题描述:
将一个n元一维向量向左旋转i个位置。例如,当n=8且i=3时,向量abcdefgh旋转为defghabc. 简单的代码使用一个n元的中间向量在n步内完成该工作。你能否仅使用数十个额外字节的存储空间,在正比于n的时间内完成该向量的旋转?

问题解析:
1、以正比于n的时间(相当于n步内)完成该操作,那么就是每个元素的移动都差不多一步到位,如将第4位的d一步移动到第1位处,其他元素也也一样。
2、额外空间只有几十个字节,这里说明并不能将前i个保存到临时空间,这样当i很大时,会消耗过多的内存空间。
3、题目条件对运行时间和内存空间都有严格的限制,

解决方案:

作者给出的方案1:求模置换的方法

#include <cstdio> #include <cstdlib> #include <cassert>     

int gcd(int a,  int b){

    assert(a > 0 && b >= 0);

    return b==0?a:gcd(b, a%b);

}void my_reverse(int arr[], int arraysize, int reversenum){

    if (reversenum == 0 || reversenum == arraysize) { return; }

    for (int i = 0; i < gcd(reversenum, arraysize); ++i){

扫描二维码关注公众号,回复: 2468931 查看本文章

        int temp = arr[i]; int j = i;

        while (1){

            int k  = j + reversenum;

            if (k >= arraysize){ k -= arraysize; }

            if (k == i) { break; }

            arr[j] = arr[k]; j = k;

        }

        arr[j] = temp;

    }

    return;

}int main(){

    int a[12] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'},  arraysize  = 12,  reversenum = 5;   

    my_reverse(a, arraysize, reversenum);

    for (int i = 0; i < 12; ++i)

        printf("%c\t", a[i]);

    return 0;

}
作者给出的方案2:(分段递归交换的方法)

作者说该段求解算法 my_reverse2是同求最大公约数的欧几里得算法是同构的,这或许对理解该段代码提供一个很好的思路,如下:  

int gcd(int i, int j){
     assert(i > 0 && j > 0);
     while (i != j){     if (i > j) i -= j;       else  j -= i;     }
     return i;
}

#include <cstdio> #include <cstdlib>      #include <ctime>      #include <cassert>    
// 函数目的:交换arr[index_a..index_a + swap_num -1]和arr[index_a..index_b + swap_num -1]
void my_swap(int arr[], int arraysize, int index_a, int index_b, int swap_num){

assert(index_a >= 0&&(index_a+swap_num) <= index_b&&(index_b+swap_num) <= arraysize);
     for (int i = 0;  i < swap_num; ++i){
          int temp = arr[index_a + i];
          arr[index_a + i] = arr[index_b + i]; 

arr[index_b + i] = temp;

}
    return;
}void my_reverse2(int arr[], int arraysize, int reversenum){
     if (reversenum == 0 || reversenum == arraysize) { return; }
     int i, j, p;
     i = p = reversenum;
     j = arraysize - p;
    while ( i != j){
         if (i > j){    my_swap(arr, arraysize, p-i, p, j);   i -= j;        }
         else {   my_swap(arr, arraysize, p-i, p+j-i, i);    j -= i;        }
     }
     my_swap(arr, arraysize, p - i, p, j);
     return;
}int main(){
    int a[12] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'};
    int arraysize  = 12;       int reversenum = 7;  
   // my_reverse(a, arraysize, reversenum);
    my_reverse2(a, arraysize, reversenum);
    for (int i = 0; i < 12; ++i)
         printf("%c\t", a[i]);
    return 0;
}

如果arrsize = 12, reversenum=5,那么该程序执行如下:
(1) p = 5, i =5, j = 7 swap(0, 7, 5) 结果:hijkl fg abcde
(2) p = 5, i =5, j = 2 swap(0, 5, 2) 结果:fg jkl hi abcde
(3) p = 5, i =3, j = 2 swap(2, 5, 2) 结果:fg hi l jk abcde
(4) p = 5, i =1, j = 2 swap(4, 6, 1) 结果:fghi k j labcde
(5) p = 5, i =1, j = 1 ---->退出循环 swap(4, 5, 1) 结果:fghi j k labcde

 

 

旋转向量x其实就是交换向量ab的两段,得到ba(a代表x中的前i个元素)。假设a比b短,将b分为b1和b2两段,使b2有跟a相同的长度,然后交换a和b2,也就是ab1b2交换得到b2b1a,a的位置已经是最终的位置,现在的问题集中到交换b2b1这两段,又回到了原来的问题。不断递归下去,到b1和b2的长度长度相等交换即可。

 

 

作者给出的方案3:(求逆法(反手算法))

利用翻转求逆的性质,得到最终的解决方案,该方案是个中最完美的一个方案。

#include <iostream> #include <cassert> using namespace std;

void my_swap(int arr[], int arrsize, int arr_a, int arr_b){

assert(arr_a >= 0 && (arr_b - arr_a >= 0) && arr_b < arrsize);

int i = arr_a,  j = arr_b;

for (; i <=j; i++,j--)

swap(arr[i], arr[j]);

}void my_reverse(int arr[], int arrsize, int reversenum){

assert(reversenum < arrsize);

my_swap(arr, reversenum, 0, reversenum - 1);

my_swap(arr, arrsize, reversenum, arrsize - 1);

my_swap(arr, arrsize, 0, arrsize - 1);

}int main(){

int a[13] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l','m' },  arraysize = 13,  reversenum = 5;    

my_reverse(a, 13, 5);

for (int i = 0; i < 13; ++i)

printf("%c\t", a[i]);

system("pause");

return 0;

}

求逆:ab->a^r b-> a^r b^r ->(a^r b^r)^r 最为简洁,也最好理解,堪称完美!

猜你喜欢

转载自blog.csdn.net/qq_30796379/article/details/81278838