一个数组A中存有N(N>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M≥0)个位置,即将A中的数据由(A0A1…AN-1)变换为(AN-M…AN-1A0A1…AN-M-1)(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法
要求
1、输入说明:第1行输入N(1≤N≤100)、M(M≥0);第2行输入N个整数。
2、输出说明:输出循环右移M位以后的整数序列。
3、测试用例:
序号 | 输入 | 输出 | 说明 |
1 | 6 2 | 5 6 1 2 3 4 | 一般情况 |
2 | 6 8 1 2 3 4 5 6 | 5 6 1 2 3 4 | M>N的情况 |
3 | 3 6 11 23 56 | 11 23 56 | M>N且正好是N的倍数 |
4 | 1 0 | 8 | 边界测试,最小的N和M |
5 | 令N为100,M为99 输入数据略 | 略 | 边界测试,最大的N和M |
四、实验分析
1、问题分析
输入的N个整数可以放在一个一维数组中。最容易想到的,也是简单的思路是循环右移一位的操作重复进行M次即可,但这种做法的数据移动次数大约是M*N次。下面“方法一”实验参考代码实现了这种简单思想。
为了减少数据的移动次数,第二种方法是通过三次倒序来巧妙地实现。为简单起见,不妨设0≤M<N(否则先进行M%=N运算即可),先把(A0A1…AN-1)倒序变成(AN-1AN-2…A1A0),再把它的前M个元素(AN-1AN-2…AN-M)倒序成(AN-M…AN-1),然后把N-M个元素(AN-M-1AN-M-2…A1A0)倒序成(A0A1…AN-M-1)。这样,整个数组就成了(AN-M…AN-1 A0A1…AN-M-1),这就是我们想要的结果。这种做法每个数据参与2次交换(倒序),所以如果一对数的交换需要3次数据移动的话,总共数据移动次数大约是3N次。下面“方法二”实验参考代码实现了这种思想。
事实上,还可以有移动次数更少的算法,实验思考题中给出了提示。
2、实现要点
M可以处理成小于N的数,以减少移动次数。当M>N时,可以用M%N代替M,效果相同,但在方法一情况下移动次数大约是(M%N)*(N+1)次。
方法一:采用循环控制结构。该方法比较简单,只需要“循环右移一位的操作重复进行M次”,先定义一个函数进行“每个元素循环右移一位的操作”,然后这个函数被重复调用M次。
方法二:采用用异或运算交换数据。该方法比较巧妙,通过三次逆转数组的部分数据就可以实现。程序中定义了一个带参数的宏Swap(a,b),用连续三次异或运算交换a与b。当然读者也可以通过引入中间变量,通过三次赋值运算实现两个数据的交换。
#include <stdio.h>
#define MAXN 100
void Shift(int Array[ ],int N);
int main( )
{
int Number[MAXN],N,M;
int i;
scanf("%d%d",&N,&M);
for(i=0;i<N;i++)
scanf("%d",&Number[i]);
M%=N; /*当M大于等于N时转化成等价的小于N的数*/
for(i=0;i<M;i++)
Shift(Number,N ); /*N个元素循环位移1位*/
for(i=0;i<N-1;i++) /*打印输出*/
printf("%d", Number[i]);
printf("%d\n", Number[N-1]);
return 0;
}
void Shift(int Array[ ],int N)
{
int i ,ArrayEnd;
ArrayEnd=Array[N-1];
for (i=N-1;i>0;i--) /*N个元素循环位移1位*/
Array[i]=Array[i-1];
Array[0]= ArrayEnd;
}