第1章 绪论

【解题指导与示例 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值