在《编程珠玑 第二版》第2章中作者给出了3个问题,其中问题B是说:能否在仅使用数十个额外字节的存储空间的情况下,在正比于n的时间内,将一个n元一维向量x向左旋转i个位置。例如:当n=8且i=3时,向量abcdefgh旋转为defghabc。
在该问题中,作者随后给出了几种解决思路,并分析了各种方法的优缺点。
思路一:使用的一个临时数组,将要移动的向量x的前i个元素复制到这个临时数组中,然后将剩下的n-i个元素向左移动i个位置,最后将最初的i个元素从临时数组中复制到向量x中余下的位置。这种方法的缺点是显然易见的,需要额外的i个存储空间,产生了过大的存储空间的消耗。
思路二:定义一个函数将向量x向左旋转一个位置(其时间正比于n)然后调用该函数i次。该方法的缺点是:由于要进行i次调用,产生过多的运行时间。
思路三:先将x[0]存储到一个临时变量temp中,然后移动x[i]到x[0],x[2i]到x[i],x[3i]到x[2i]依此类推(其中x中所有的坐标都需对n取模),直到当x[(k*i)%n]中k*i%n取回0时,然后取而代之以temp值移动到x[(k-1)*i%n]终止该过程。如果该过程没有移动全部元素,就从x[1]开始重复上述过程,直到所有的元素都已经移动为止。
思路四:利用旋转法,将要旋转的向量x可看成是ab,其中a代表x中的前i个元素,则旋转向量x其实就是交换向量ab的两端,得到向量ba。书中提到我们可以先对a求逆得到(a)Tb,再对b求逆得到(a)T(b)T,然后整体求逆((a)T(b)T)T,记得ba。
本文中给出了思路三和思路四的解法,思路三的程序执行流程为:
其中,置换轮次为什么为旋转位数和向量长度的最大公约数,这里有一篇写的比较好的博文,大家可以参考:http://kb.cnblogs.com/page/88517/
以下为代码:
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
int gcd(const int& a, const int& b);
void VectorRotation(char* str, const int& step, const int& Len);
void Reverse(char* str, const int& Len);
void VectorRotation_Reverse(char* str, const int& step, const int& Len);
int _tmain(int argc, _TCHAR* argv[])
{
char str[] = "abcdefghijklmnopqrstuvwxyz";
cout<<"the raw string is: "<<str<<endl<<endl;
/*
VectorRotation(str, 3, strlen(str));
cout<<"the string Rotation Left 11 postion is: "<<str<<endl;
*/
VectorRotation_Reverse(str, 3, strlen(str));
cout<<"the string Rotation Left 3 postion is: "<<str<<endl;
for(;;);
return 0;
}
/*****************************************************************************************************
*Function: VectorRotation(char* str, int& i)
*Author: Sky
*Descrition: 向量左旋转i个位置(杂技算法,对于字符来说只需一个字节的额外存储空间)
*Access Level:
*Input: str: 旋转向量
step:旋转位移量
Len: 字符串长度
*Output: NULL
*Return: NULL
*****************************************************************************************************/
void VectorRotation(char* str, const int& step, const int& Len)
{
char temp = ' ';
int j = 0; // 置换索引
int k = 0; // 当前待置换的位置
int round = gcd(step, Len); // 置换轮次
for(int i=0; i!=round; i++)
{
temp = str[i];
do
{
str[(j*step+i)%Len] = str[((j+1)*step+i)%Len];
j++;
k = ((j+1)*step+i)%Len;
}
while(i != k); // 直至返回到取str[i]中的元素
str[(j*step+i)%Len] = temp;
j = 0; // 复位,为下一轮次做准备
}
}
/*****************************************************************************************************
* 求两个数的最大公约数 欧几里得算法又叫辗转相除法
* m=n*q+r; gcd(m,n)=gcd(n,r);
******************************************************************************************************/
int gcd(const int& a, const int& b)
{
if(0 == b)
return a;
else
{
int Remainder = a%b;
return gcd(b, Remainder);
}
}
/************************************************************************************************
* 3、反转算法 AB ((A)T(B)T)T=BA
*************************************************************************************************/
void VectorRotation_Reverse(char* str, const int& step, const int& Len)
{
Reverse(str, step);
Reverse(str+step, Len-step);
Reverse(str, Len);
}
void Reverse(char* str, const int& Len)
{
char* first = str;
char* last = str+Len-1;
char temp = ' ';
while(first < last)
{
// switch
temp = *first;
*first = *last;
*last = temp;
// next
first++;
last--;
}
}