【解题指导与示例 7】
/*
下面前四种方案代码实现借鉴了《数据结构及应用算法教程》(严蔚敏 陈文博)
本程序功能是实现一个长度为n的数组左移p位,且0<p<n(看下面代码注意此条件)
a[0],a[1],...,a[n-1]变为a[p],a[p+1],...,a[n-1],a[0],..,a[p-1]
测试以n=10,p=6,a[i]=i+1,i=0,1,2,...,9为例
*/
#include<iostream>
#include<algorithm>
using namespace std;
//方案一:先写一个左移一位的函数,再执行这个函数p次
void left_shift_1unit(int a[],int n){
int tmp=a[0];
for(int i=1;i<n;i++)
a[i-1]=a[i];
a[n-1]=tmp;
}
void left_shift1(int a[],int n,int p){
for(int i=1;i<=p;i++)
left_shift_1unit(a,n);
}
//方案二:观察移动前后的两个排列,先把a[0]到a[p-1]存到一个新建的
//数组中,a[p]到a[n-1]移动完成后,再把a[0]到a[p-1]接到后面去
void left_shift2(int a[],int n,int p){
int *b=new int[p];
if(b==nullptr)exit(-1);
for(int i=0;i<p;i++)
b[i]=a[i];
for(int i=p;i<n;i++)
a[i-p]=a[i];
for(int i=n-p,cnt=0;i<n;i++,cnt++)
a[i]=b[cnt];
delete[] b;
}
//下面的方案三如果用1,2,3,...,12,p=3更直观,可以手写下
//方案三:先手写一下,1,2,3,4,5,6,7,8,9,10移动次序是:1,1+6=7,(1+6*2)%10=3,(1+6*3)%10=9,
//(1+6*4)%10=5,(1+6*5)%10=1,回到了1,再从2开始移动次序:2,2+6=8,(2+6*2)%10=4,(2+6*3)%10=0,
//(2+6*4)%10=6,(2+6*5)%10=2,回到了2,至此,进过两趟移动,所有的元素都完成了左移6位。通过这
//个实例,我们最想解决的问题是,对于一般的移动,我们要移动多少趟,每趟有多少个元素发生移动,
//只要解决了这两个疑问,这个算法就可以实现了。推导如下:
//设移动x趟,每次移动元素个数是M,x*M=n数组下标从0开始记,看第一趟,如果能返回到出发点下标
//为0的元素,则总存在M使得,M*p=n*d(n的倍数)
//这里设n,p的最大公约数为g,n=g*a,p=g*b,a,b互质,M*g*b=g*a*d,即M=a*d/b,则最小的M=a,则x=n/M=n/a=g
int gcd(int a,int b){
if(b==0)return a;
return gcd(b,a%b);
}
void left_shift3(int a[],int n,int p){
int g=gcd(n,p);
int M=n/g;
for(int i=0;i<g;i++){
int tmp=a[i];
int j=i;
for(int m=1;m<M;m++){
int k=(m*p+i)%n;
a[j]=a[k];
j=k;
}
a[j]=tmp;
}
}
//方案四:观察移动前后,发现只需要把原来的排列逆置,再把前n-p个元素逆置,再把
//后p个元素逆置即可。
//下面的i,j是数组下标
void invert(int a[],int i,int j){
for(int k=i;k<=(i+j)/2;k++)
swap(a[k],a[i+j-k]);
}
void left_shift4(int a[],int n,int p){
invert(a,0,n-1);
invert(a,0,n-p-1);
invert(a,n-p,n-1);
}
//方案五:一步到位完成左移,放在新的一个数组中,再放回到a中即可
void left_shift5(int a[],int n,int p){
int *b=new int[n];
if(b==nullptr)exit(-1);
for(int i=0;i<n;i++)
b[(i+n-p)%n]=a[i];
for(int i=0;i<n;i++)
a[i]=b[i];
delete[] b;
}
//初始化数组为1,2,...,10
void init(int a[]){
for(int i=0;i<10;i++)
a[i]=i+1;
}
//打印出移动后的数组
void print(int a[]){
for(int i=0;i<10;i++){
if(i==0)cout<<a[i];
else cout<<" "<<a[i];
}
cout<<endl;
}
int main()
{
int a[10];
//方案一
init(a);left_shift1(a,10,6);print(a);
//方案二
init(a);left_shift2(a,10,6);print(a);
//方案三
init(a);left_shift3(a,10,6);print(a);
//方案四
init(a);left_shift4(a,10,6);print(a);
//方案五
init(a);left_shift5(a,10,6);print(a);
return 0;
}