编程珠玑:向量旋转(旋转交换)

问题描述 数组

请将一个具备n个元素的一维向量向左旋转i个位置。例如,假设n=8,i=3,那么向量abcdefgh旋转以后获得向量defghabc。简单编码使用一个具备n个元素的中间向量分n步便可完成此做业。你能够仅使用几十字节的微小内存,花费与n成比例的时间来旋转该向量吗? 函数

解决思路 编码

方案一: code

将向量x中的前i个元素复制到一个临时数组中,接着将余下的n-i个元素左移i个位置,而后再将前i个元素从临时数组中复制回x中的后面位置。 递归

该方案使用了i个额外的位置,如i足够大,过于浪费空间。 内存

方案二: io

定义一个函数来将x向左旋转一个位置,而后调用该函数i次。 class

该方案须要将数组移到i将,过于浪费时间。 效率

方案三: 变量

先将x[0]移临时变量t中,而后将x[i]移到x[0]中,x[2i]移到x[i]中,依次类推,直到咱们又回到从x[0]中提取元素,不过在这时咱们要从t中提取元素,而后结束该过程。当i=3,n=12时,该阶段将如下面的次序移到各个元素。

若是该过程不能移动全部的元素,那么咱们再从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获得(arbrr,实际上就是ba。

reverse(0, i-1)   /*cbadefgh*/
reverse(i, n-1)  /*cbahgfed*/
reverse(0, n-1) /*defghabc*/

该转置代码在时间和空间上都颇有效,而且是这么简短和简单,想出错都很难。

书中还提供了将10个元素的数组向上旋转5个位置的手摇法例子:先是掌心对着你本身,左手在右手上面,如图所示

代码实现

#include <stdio.h>

void swap(int *p, int *q);
void reverse(int *vector, int index_begin, int index_end);
void revrot(int *vector, int len, int step);

int main(int argc, char **argv)
{
   int  step = 3;
   int  i = 0;
   int  vector[1024] = {1,2,3,4,5,6,7,8};

   revrot(vector, 8, step);

   printf("after revolve: ");
   for(i = 0; i < 8; i++)
   {
      printf("%d ", vector[i]);
   }
   printf("\n");
}

void swap(int *p, int *q)
{
   int temp;

   temp = *p;
   *p = *q;
   *q = temp;
}

void reverse(int *vector, int index_begin, int index_end)
{
   while(index_begin < index_end)
   {
      swap(vector + index_begin, vector + index_end);
      index_begin++;
      index_end--;
   }
}

void revrot(int *vector, int len, int step)
{
   reverse(vector, 0, step - 1);
   reverse(vector, step, len - 1);
   reverse(vector, 0, len - 1);
}
相关文章
相关标签/搜索