编程珠玑第二章问题B提到了n元一维向量的旋转问题,并且提出了5种思路.
问题描述:
将一个n元一维向量向左旋转i个位置。例如当n=8且i=3时,向量abcdefgh旋转为defghabc。简单的代码使用一个n元的中间向量在n步内完成该工作。你能否仅使用数十个额外字节的存储空间,在正比于n的时间内完成向量的旋转?
思路1:开辟一个长度为i(这里说的i介于[0-n]之间,超过这个范围需要对n取模,n为字符串s的长度)的临时空间存储s[0-i),然后将s向左移动i个单位,最后将临时空间中的数据放到s的后半部分。该方法空间消耗比较大O(i),代码如下:
void rotate(string &s, int rotdist){
int *tmp = new int[rotdist], i, len = s.length();
for(i = 0; i < rotdist; ++i)
tmp[i] = s[i];
for(i = rotdist; i < len; ++i)
s[i-rotdist] = s[i];
for(i = len - rotdist; i < len; ++i)
s[i] = tmp[rotdist+i-len];
}
思路2:时间换空间,每一次向左移动1个单位,总共移动i(同上述i)次,这样的空间复杂度为O(1),代码如下:
void rotateOnce(string &s){
int t = s[0];
for(int i = 0; i < s.length()-1; ++i)
s[i] = s[i+1];
s[s.length()-1] = t;
}
void rotate(string &s, int rotdist){
for(int i = 0; i < rotdist; ++i)
rotateOnce(s);
}
思路3:要在有限的资源内解决该问题,显然需要更加复杂的程序。书中,提到一个“杂技”方法:移动s[0]到临时变量t,然后移动s[i]至s[0],s[2i]至s[i],一次类推(将s中的所有下标对n取模),直至返回到取s[0]中的元素,此时改为从t取值然后终止过程。代码如下:
int gcd(int a, int b)//最小公约数
{
while (a != b)
{
if (a > b)
a -= b;
else
b -= a;
}
return a;
}
void acrobatics(string &s, int rotdist){
int t, j, i, k, n = s.length();
int mod = gcd(rotdist, n);
for(i = 0; i < mod; ++i){
t = s[i];
j = i;
while(1){
k = j + rotdist;
if(k >= n)
k -= n;
if(k == i)
break;
s[j] = s[k];
j = k;
}
s[j] = t;
}
}
思路4:从另外一面考察这个问题,可以得到一个不同的算法:旋转向量s其实就是交换向量ab的两段,得到向量ba。这里a代表s中的前i个元素。假设a比b短,将b分成bl和br,是的br具有与a相同的长度。交换a和br,也就将a(bl)(br)转换成(br)(bl)a。序列a此时位于其最终的位置,因此现在的问题就集中到交换b的两个部分。由于新问题与原来的问题具有相同的形式,因此可以递归求解。代码如下:
void swapBlock(string &s, int a, int b, int m){//表示交换s[a,a+m)和s[b,b+m)
for(int i = 0; i < m; ++i){
swap(s[a+i], s[b+i]);
}
}
void rotate(string &s, int rotdist){
int i, p, j, n;
n = s.length();
if(rotdist < 0)
rotdist += n;
i = p = n - rotdist % n;
j = n - p;
while(i != j){
cout<<i<<"\t"<<j<<endl;
if(i > j){
swapBlock(s, p-i,p, j);
i -= j;
}
else{
swapBlock(s, p-i, p+j-i, i);
j -= i;
}
}
swapBlock(s, p-i, p, i);
}
思路5:翻手法(求逆法)推荐的方法,快速易懂。将问题看作是把数组ab转换成ba,同时假定我们拥有一个函数可以将数组中特定部分的元素求逆。从ab开始,首先对a求逆,得到
reverse(0, i-1) /*cbadefgh*/
reverse(i, n-1) /*cbahgfed*/
reverse(0, n-1) /*defghabc*/
代码如下:
void reverse(string &s, int start, int end){//reverse s[start, end]
int i = start, j = end;
while(i < j){
swap(s[i], s[j]);
i++;
j--;
}
}
void reverseRotation(string &s, int rotdist){
reverse(s, 0, rotdist - 1);
reverse(s, rotdist, s.length() - 1);
reverse(s, 0, s.length() - 1);
}

8704

被折叠的 条评论
为什么被折叠?



