向量旋转问题

问题:

《编程珠玑》里面典型的向量旋转问题。

分析:

直接转载的,认为写的比较好

--------------------------------------------------------

问题描述如下:Rotate vector x[n] left by d positions. 把x[n]向左旋转d个位置。

例如:当n=8, d=3, 把abcdefgh向左旋转3位成defghabc.

首先d=d%n:

解法一:Pricey Solutions  时间复杂度和空间复杂度均为0(n)

用O(d)的临时空间存储x[1-d], 把剩余元素向左移动, 然后再把临时空间移到x后面去。

时间复杂度:O(n); 空间复杂度为 O(d) = O(n);

解法二:A Juggling Algorithm

移动x[0]到temp, 移动x[d]到x[0]的位置, 移动x[2d]到x[d]的位置,x[(k+1)d%n]移动到x[kd%n]的位置,如下图所示。

image

解法三:Block-swap Algorithm

旋转向量x其实就是交换向量ab的二段得到ba,其中a代表x的前d个元素。例如abcdefgh向左旋转3位成defghabc,即交换(abc)和(defgh).

假设a比b短,则可以将b分为二段[bl]和[br]其中[br]长度和a相同。交换ab即为交换a[bl][br],则可首先交换a和[br]得到[br][bl]a,a已经处于正确位置,接下来可递归需交换[br][bl],从而得[bl][br]a即ba。

解法四:The Reversal Algorithm

从解法三看到交换ab,其实可以先翻转a得到a'然后翻转b得到b‘,最后将a'b'整体翻转ba。

例如:abcdefgh=>[cba][hgfed]=>[defghabc]

代码如下:

/**
*
*  Rotate vector x[n] left by d positions
*
**/

#include <stdio.h>

/**
* Algorithm 1: pricey algorithm
* O(n) time and O(n) extra space
**/
 

void pricey(int x[], int n, int d)
{
    d
= d % n;
   
int * temp = new int[d];
   
for(int i = 0; i < d; i++)  //move the first d elements into the temp
   
{
        temp
[i] = x[i];
   
}
   
for(int i = d; i < n; i++)  //move the last n-d elements into the first n-d places
   
{
        x
[i-d] = x[i];
   
}
   
for(int i = 0 ; i < d; i++) //move the temp into the last d elements
   
{
        x
[n-1-i] = temp[d-1-i];
   
}
   
delete[] temp;
}

/**
* Algorithm 2: A juggling algorithm
* O(n) time and O(1) extra space
**/

void swap(int * a, int *b)
{
   
int temp = *a;
   
*a = *b;
   
*b = temp;
}
int gcd(int n, int d)
{
   
if(n < d) swap(&n, &d);
   
if(n % d == 0) return d;
   
else return gcd(d, n/d);
}
void juggling(int x[], int n, int d)
{
   
int temp; //store the x[i]
   
int j;
   
for(int i = 0; i < gcd(n, d); i++)
   
{
        temp
= x[i];
        j
= i;
       
while(1)
       
{
           
int k = (j + d)%n;
           
if( k == i) break;
            x
[j] = x[k];
            j
= k;
       
}
        x
[j] = temp;
   
}
}

/**
*Algorithm 3: The Block-Swap Algorithm
*The idea: change ab to ba
*If a is shorter, divide b into bl and br
*Swap a and br to change a[bl][br] into [br][bl]a
*Then recusive to change [br][bl]
* O(n) time and O(1) extra space
**/

//swap x[a,a+len-1]and x[b,b+len-1]
void swap_block(int x[], int a, int b, int len)
{
   
for(int i = 0; i < len; i++)
   
{
        swap
(x+a+i,x+b+i);
   
}
}
void blockswap(int x[], int n, int d)
{

    d
= d % n;
   
if( n ==d || d == 0 ) return;
   
else
   
{
       
int a = 0;
       
int b = d;
       
int a_len = d;
       
int b_len = n - d;
       
while(1)
       
{
           
if( a_len > b_len)
           
{
                swap_block
(x, a, b, b_len);
                a
=  a + b_len;
                a_len
= a_len - b_len;
           
}
           
else if( a_len < b_len)
           
{
                swap_block
(x, a, b + b_len - a_len, a_len);
                b_len
= b_len - a_len;
           
}
           
else
           
{
                swap_block
(x, a, b + b_len - a_len, a_len);
               
break;
           
}
       
}

   
}
}

/**
*Algorithm 4: The Reversal Algorithm
*The Idea
*Reverse a to get a rb.
*Reverse b to get a rb r .
*Reverse all to get (a rb r ) r = ba.
*O(n) time and O(1) extra space
**/

void reverse(int x[], int start, int end)
{
   
for(int i = start, j = end; i < j; i++, j--)
   
{
        swap
(x+i, x+j);
   
}
}
void reversal(int x[], int n, int d)
{
    reverse
(x, 0, d-1);
    reverse
(x, d, n-1);
    reverse
(x, 0, n-1);
}

int main()
{
   
int x[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
   
int n = 8;
   
int d = 3;
    reversal
(x, n, d);

   
for(int i = 0; i < n; i++)
   
{
        printf
("%c " , x[i]);
   
}
    printf
("\n");
   
return 0;
}

出处:http://wansishuang.appspot.com/?p=77001

---------------------------------------------------------

第二种方法时,用到了gcd,循环次数是n和d的最大公约数,如果n和d最大公约数是1,那么循环只要执行一次就能将所有的字符移动到指定位置,这里还是非常巧妙的。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值