编程珠玑 旋转字符串
2016-04-25 16:03
344 查看
问题描述
请将一个具有n个元素的一维向量向左旋转i个位置。例如,假设n=8,i=3,那么向量abcdefgh旋转之后得到向量defghabc。简单编码使用一个具有n个元素的中间向量分n步即可完成此作业。你可以仅使用几十字节的微小内存,花费与n成比例的时间来旋转该向量吗?
解决思路
方案一:
将向量x中的前i个元素复制到一个临时数组中,接着将余下的n-i个元素左移i个位置,然后再将前i个元素从临时数组中复制回x中的后面位置。
该方案使用了i个额外的位置,如i足够大,过于浪费空间。
方案二:
定义一个函数来将x向左旋转一个位置,然后调用该函数i次。
该方案需要将数组移到i将,过于浪费时间。
方案三:
先将x[0]移临时变量t中,然后将x[i]移到x[0]中,x[2i]移到x[i]中,依次类推,直到我们又回到从x[0]中提取元素,不过在这时我们要从t中提取元素,然后结束该过程。当i=3,n=12时,该阶段将以下面的次序移到各个元素。
![](http://static.oschina.net/uploads/space/2012/1114/195708_TL3n_101347.jpg)
如果该过程不能移动所有的元素,那么我们再从x[1]开始移动,一直依次进行下去,直到移动了所有的元素时为止。
该方案过于精巧,像书中所说的一样堪称巧妙的杂技表演,非常容易出错。
代码实现:
方案四:
旋转向量x实际上就是将向量ab的两个部分交换为向量ba,这里a代表x的前i个元素。假设a比b短。将b分割成bl和br,使br的长度和a的长度一样。交换a和br,将ablbr转换成brbla。因为序列a已在它的最终位置了,所以我们可以集中精力交换b的两个部分了。由于这个新问题和原先的问题是一样的,所以我们以递归的方式进行解决。
该方案要求编码细腻,还需要深思熟虑,以确保程序具有足够的效率。
方案五:(最佳)
将这个问题看作是把数组ab转换成数组ba吧,但同时也假定我们具有在数组中转置指定部分元素的函数。我们先从ab开始,转置a得到arb,再转置b得到arbr,然后再转置整个arbr得到(arbr)r,实际上就是ba。
reverse(0, i-1) /*cbadefgh*/
reverse(i, n-1) /*cbahgfed*/
reverse(0, n-1) /*defghabc*/
该转置代码在时间和空间上都很有效,并且是这么简短和简单,想出错都很难。
书中还提供了将10个元素的数组向上旋转5个位置的手摇法例子:先是掌心对着你自己,左手在右手上面,如图所示
![](http://static.oschina.net/uploads/space/2012/1114/201106_3tdL_101347.jpg)
代码实现
?
请将一个具有n个元素的一维向量向左旋转i个位置。例如,假设n=8,i=3,那么向量abcdefgh旋转之后得到向量defghabc。简单编码使用一个具有n个元素的中间向量分n步即可完成此作业。你可以仅使用几十字节的微小内存,花费与n成比例的时间来旋转该向量吗?
解决思路
方案一:
将向量x中的前i个元素复制到一个临时数组中,接着将余下的n-i个元素左移i个位置,然后再将前i个元素从临时数组中复制回x中的后面位置。
该方案使用了i个额外的位置,如i足够大,过于浪费空间。
方案二:
定义一个函数来将x向左旋转一个位置,然后调用该函数i次。
该方案需要将数组移到i将,过于浪费时间。
方案三:
先将x[0]移临时变量t中,然后将x[i]移到x[0]中,x[2i]移到x[i]中,依次类推,直到我们又回到从x[0]中提取元素,不过在这时我们要从t中提取元素,然后结束该过程。当i=3,n=12时,该阶段将以下面的次序移到各个元素。
![](http://static.oschina.net/uploads/space/2012/1114/195708_TL3n_101347.jpg)
如果该过程不能移动所有的元素,那么我们再从x[1]开始移动,一直依次进行下去,直到移动了所有的元素时为止。
该方案过于精巧,像书中所说的一样堪称巧妙的杂技表演,非常容易出错。
代码实现:
#include "stdafx.h" #include<iostream> #include <string> #define MAX 8 #define I 3 using namespace std; int main() { int a[MAX]; //向数组输入值 for (int t = 0;t < MAX;t++) a[t] = t; const int n = MAX, i =I;//指定两个变量n和i int temp ; int counter = 0;//计数,看是否到了n int index = -1;//指示一个内层循环没有完成,就从index处开始,重复算法 int j = 0;//代表累计的和,比如n=8,i=3时,j依次为0,8,16,24,32.。。 int k ;//计数的变量,0,1,2,3,,,用于表示i的倍数 int tempJ;//保存最后一步j的位置 while (counter != n) { index++; temp = a[index]; k = 1; while ((j+i)%n != index) { a[j] = a[(index + k*i) % n]; tempJ = (j+i)%n; j = (index + k*i) % n; k++; counter++; } a[tempJ] = temp; counter++;//这里照样移动一次 j = index+1; } //测试输出 for (int ii = 0;ii < MAX; ii++) cout << a[ii]<<" "; int t; cin >> t; return 0; }
方案四:
旋转向量x实际上就是将向量ab的两个部分交换为向量ba,这里a代表x的前i个元素。假设a比b短。将b分割成bl和br,使br的长度和a的长度一样。交换a和br,将ablbr转换成brbla。因为序列a已在它的最终位置了,所以我们可以集中精力交换b的两个部分了。由于这个新问题和原先的问题是一样的,所以我们以递归的方式进行解决。
该方案要求编码细腻,还需要深思熟虑,以确保程序具有足够的效率。
方案五:(最佳)
将这个问题看作是把数组ab转换成数组ba吧,但同时也假定我们具有在数组中转置指定部分元素的函数。我们先从ab开始,转置a得到arb,再转置b得到arbr,然后再转置整个arbr得到(arbr)r,实际上就是ba。
reverse(0, i-1) /*cbadefgh*/
reverse(i, n-1) /*cbahgfed*/
reverse(0, n-1) /*defghabc*/
该转置代码在时间和空间上都很有效,并且是这么简短和简单,想出错都很难。
书中还提供了将10个元素的数组向上旋转5个位置的手摇法例子:先是掌心对着你自己,左手在右手上面,如图所示
![](http://static.oschina.net/uploads/space/2012/1114/201106_3tdL_101347.jpg)
代码实现
?
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析