PTA - 数组元素循环右移问题

 

题目:

一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0​A1​⋯AN−1​)变换为(AN−M​⋯AN−1​A0​A1​⋯AN−M−1​)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?

输入格式:

每个输入包含一个测试用例,第1行输入N(1≤N≤100)和M(≥0);第2行输入N个整数,之间用空格分隔。

输出格式:

在一行中输出循环右移M位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。

输入样例:

6 2
1 2 3 4 5 6

输出样例:

5 6 1 2 3 4

代码: 

#include <stdio.h>

int main() {
    int N, M;  //N是输入元素的个数,M是右移的位数
    scanf("%d%d", &N, &M);

    int arr[N + 1];
    for (int i = 1; i <= N; i++) {
        scanf("%d", &arr[i]);
    }

    M %= N;
    if (M == 0)
        M = N;

    int i;
    i = N - M + 1;

    int cnt = 0;
    while (1) {
        if (cnt != 0)
            printf(" ");
        printf("%d", arr[i]);
        cnt++;
        
        if (i == N)
            i = 1;
        else
            i++;

        if (i == N - M + 1)
            break;
    }

    return 0;
}

分析:

        上述代码没有涉及翻转数组元素。

本题给的样例分析

6 2
1 2 3 4 5 6

        M代表移动的位数,当移动位数是2,则从最后一位往前数,倒一是“6”,倒二是“5”。

        找到倒二位之后,从倒二位开始输出,当输出的元素是最后一位(示例为“6”)的时候,从头开始输出(示例为“1”),然后一直输出,一直输出,直到倒二的前一位,停止。 

5 6 1 2 3 4

 本篇文章代码分析

/* 代码一 */
int arr[N + 1];
for (int i = 1; i <= N; i++) {
    scanf("%d", &arr[i]);
}

        代码一中,定义了一个用来存储用户输入的数的数组,不同的是,这个数组没有用上arr[0],而是从下标为1开始直到下标为N+1(下图)。

2c780811f5fb1b05f1dc6d4c8049e5a1.png

/* 代码二 */ 
M %= N;
if (M == 0)
    M = N;
int i;  //i代表输出的第一个数在数组中的下标
i = N - M + 1;

        代码二中,主要的作用在于,当我们输入的M(也就是右移的位数)大于等于N时,我们要再次从尾巴开始数,比如题目给的示例:6个数“ 1 2 3 4 5 6 ”。M为2时,从倒二“5”开始输出;M为5时,从倒五“2”开始输出;M为8时,数完一轮剩2,再次从倒一数,然后倒二“5”,最后从“5”开始输出。所以这就是为什么要取余的原因,“M = 8” % “N = 6” = “M = 2”。

        但是!当M刚好是N或者是N的倍数或者是0时,取余之后M为0,这时候是要从第一位开始输出的。而N = 6, M = 0或6或12或18...,取余后“i = N - M + 1”这时候会等于7,我们知道应该是要从一开始,所以让M由0变成N,多减一次N就可以。(为什么“i = N - M + 1”中有加一?参考代码一解释。)

/* 代码三 */
while (1) {
    if (cnt != 0)
        printf(" ");
    printf("%d", arr[i]);
    cnt++;
    
    if (i == N)
        i = 1;
    else
        i++;

     if (i == N - M + 1)
        break;
}

        代码三应该是比较好理解的。

        while(1)表示一直循环,循环出口是当遍历完一遍之后,下个元素又是最开始的那个元素,就停止。

        值得注意的是,是“i”先加,然后才判断下一个是不是最开始的那个元素(或者说是否已经遍历过了)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值