顺序移动数组元素(最大公约数的应用)

问题描述:
数组里有n个数据,要将他们顺序循环向后移k位,即前面元素向后移动k位,后面的元素向前移动k位。由于n可能很大,不允许用2*n以上的空间完成此题。

法1:算法设计:可开辟另外一个与所需处理数组空间大小相同的的数组来存处理后的元素。循环过程中,0~n-k-1个元素的确是向后移动,而后面的则是顺序从后向前移动。因此用取模运算就可以决定每个元素移动后的的最终位置。
程序代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define N 10

int main(){
    int n,k,i;
    int a[N],b[N];

    printf("输入数组中数的个数:");
    scanf("%d",&n);
    printf("\n输入数的移动步数:");
    scanf("%d", &k);;
    printf("\n输入每个数字:");
    for (i = 0; i < n; i++)
        scanf("%d", &a[i]);

    for(i=0;i<n;i++)
    b[(i+k)%n]=a[i];

    printf("\n移动后输出:");
    for (i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }

    system("pause");
    return 0;
}

法2:算法设计:将最后一个元素用临时变量存储,其余元素逐个向后移动一个,然后用临时变量最后一个元素放入第一个空间中,把这个过程重复k次,则达到要求。
程序代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define N 10

int main(){
    int n,k,i,j;
    int a[N];
    printf("输入数组中数的个数:");
    scanf("%d",&n);
    printf("\n输入数的移动步数:");
    scanf("%d", &k);;
    printf("\n输入每个数字:");
    for (i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }

    for(i=0;i<k;i++)
    {b=a[n-1]
    for(j=n-1;j>=0;j--)
        a[j]=a[j-1];
    a[0]=b;
    }

    printf("\n移动后输出:");
    for (i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }

    system("pause");
    return 0;
}

法3:(算法书上)利用一个临时的存储空间,把每一个数据一次移动到位。
把位置差为所需移动步长的分为一个组,进行移动。因此要求出组数和一个组里有多少个元素,然后进行组内移动。
例如:
当元素个数为6,移动步长为3时,0,1,2,3,4,5移动的结果是3,4,5,0,1,2。则移动组数可分为3组:(0->3,3->0)(1->4,4->1)
(2->5,5->2)。当移动步长为2时,可分为2组:(0->2,2->4,4->0) (1->3,3->5,5->1)
由实例分析,循环组数为元素个数和步长的最大公约数,每组的个数也可由元素个数除以组数算出。
为什么组数是最大公约数呢?书上没有说明,因此我试着分析一下。
一组数据中,最后一个元素肯定是又回到第一个元素上的,因此组数肯定是要能被元素的个数整除的,由于每个元素之间是隔了k个(步长)元素,因此一组元素加上每个元素下标取余元素总数都应为相同的数才为一组,因此组数为元素个数和步长的最大公约数。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define N 10

int couculateM(int x,int y)
{
    int r;
    while (y)
    {   r = x%y;
        x = y;
        y = r;
    }
    return x;
}
int main(){
    int n,m,k,i,b,b1,t,j;
    int a[N];
    printf("输入数组中数的个数:");
    scanf("%d",&n);
    printf("\n输入数的移动步数:");
    scanf("%d", &k);;
    printf("\n输入每个数字:");
    for (i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    m = couculateM(n, k);
    for (i = 0; i < m; i++)
    {
        b = a[i];
        t = i;
        for (j = 0 ; j <n/m; j++)
        {
            t=(t+k)%n;
            b1=a[t];
            a[t] = b;
            b = b1;
        }
    /*  从前往后移会用b、b1两个临时变量,从最后一个开始,把前一个以此向
        后移,与法2的思想一样,只用一个临时变量就可解决问题。并且减少
        赋值次数,具体实现。
    for (i = 0; i < m; i++)
    {
        b = a[(i+(n/m-1)*k)%n];
        t = i + (n / m - 1)*k;
        for (j = n/m; j >0; j--)
        {

            a[t%n] = a[(t - k) % n];
            t = t - k;
        }
        a[i] = b;

    }*/

    }
    printf("\n移动后输出:");
    for (i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }
    system("pause");
    return 0;
}

总结:
法1算法思想很简单,也很好理解,但会占用一个元素空间。
法2重复做一件简单事情,简化了问题,但是重复次数太多,效率并不高。
法3为分而治之的思想,将元素分为m各组,每组重复相同的事情,效率比前面两种方法都高

转载于:https://blog.51cto.com/14240895/2366284

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值