n元一维向量旋转问题(编程珠玑--第2章--问题B )

在《编程珠玑 第二版》第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--;
	}
}


程序执行结果:




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值