问题描述:
一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1⋯AN−1)变换为(AN−M⋯AN−1A0A1⋯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
题目分析:单从结果来看,很容易想到的是,只需要实际输出的时候按题目所要求的那样输出,不去实际改变序列的顺序,就能通过,这是投机的方法。其次,如果没有时间,空间复杂度的要求,那另外开辟一个空间,然后按照想要的顺序移进去也可以,但是题目中希望考虑 。最后不知道各位能不能想到数据结构,大概是在线性表的逆置那块,遇到过类似的操作,稍加改动就可以实现这个题目。(另外,提一句,考研真题中出现过和此题几乎一样的题,不过要求是左移p个位置。思想一定要掌握,真题中难度稍大一点,但核心一样)。
方法小结:①printf输出的时候投机,按似乎右移的结果输出;
②不考虑空间复杂度的时候,开辟一个新的数组,按照结果依次移进去;
③利用数据结构中链表逆置的思想。
下面主要介绍第三种方法:假设数组num[n],需要右移m位,需要实现三次逆置,设逆置函数为:
void reverse(int num[],int left,int right,int k){
int temp;
for(int i=left,j=right;i<left+k&&i<j;i++,--j){ //
temp = num[i];
num[i] = num[j];
num[j] = temp;
}
}
- reverse(num,0,m-1,m);
- reverse(num,m,n-1,n-m);
- reverse(num,0,n-1,n);
第一次,将m之前的元素全都逆置,第二次,将m之后的元素逆置,第三次将表内所有元素全都逆置。
注意:
题目中输入的M,只是说将整组数字右移M位,并不是说以num[m]这个数字开始,向右移动M位,这一点很重要,因为一开始我下意识的这样认为,导致结果错误。其实我们必须先找到那个第一个向右移的元素打头,当作m,然后用上三次逆置就能解决问题。
真正的m应该是n-m;
#include<iostream>
#include<vector>
using namespace std;
void reverse(int num[],int left,int right,int k){
int temp;
for(int i=left,j=right;i<left+k&&i<j;i++,--j){
temp = num[i];
num[i] = num[j];
num[j] = temp;
}
}
int main()
{
int n,m;
cin >> n >>m;
int num[n];
for(int i=0;i<n;++i)
{
cin >> num[i];
}
m=m%n; //题目中给的m是有可能大于n的;少了这一步,将会扣6分;
m = n-m; //找到m;
reverse(num,0,m-1,m);
reverse(num,m,n-1,n-m);
reverse(num,0,n-1,n);
for(int i=0;i<n-1;i++){
cout<<num[i]<<" ";
}
cout << num[n-1];
return 0;
}
小结:
此类问题都可以用方法三得到解决,关键点在于m的位置,找到正确的m位置,然后三次逆置,就能得到正确结果。可以试一下左移是如何实现的。(左移的m就是给出的m,更简单一点。)
补充
关于reverse函数,为什么写的复杂,可以有更简单版本,如reverse(int num[],int left,int right);也使用于本题,文章提供的reverse函数,多出来的参数k含义是到第k个元素为止,看如下例题:
有的时候我们会遇到的情况是:需要你移动的元素是前k个,和数组后端的k个顺序或者逆序交换,此时会用到参数k,但是其实这类题的实现思想和本题是类似的,只不过限制条件有变化。例题的难度高于PAT的难度,所以加以改造,例题的函数完全可以解决问题。
再补充一下一道考研数据结构的真题:
这都属于同一类型的题,思想的核心大同小异,考研里面,不属于难题的一类,需要完全透彻的理解掌握。