数组元素循环右移问题 (20 分)
文章目录
1. 题目描述
一个数组 A 中存有 N( > 0 )个整数,在不允许使用另外数组的前提下,将每个整数循环向右移 M( ≥ 0 )个位置,即将 A 中的数据由( A0,A1 … AN-1 ) 变换为( AN-M … AN-1,A0,A1… AN-M-1 )(最后 M 个数循环移至最前面的 M 个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
2. 输入格式
每个输入包含一个测试用例,第 1 行输入 N( 1 ≤ N ≤ 100 )和 M( ≥ 0 );第 2 行输入 N 个整数,之间用空格分隔。
3. 输出格式
在一行中输出循环右移 M 位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
4. 输入样例
6 2
1 2 3 4 5 6
5. 输出样例
5 6 1 2 3 4
6. 解题思路
此处可以参看我另外一篇有稍微详细的介绍数组元素循环移动的文章。
数组元素循环移动
6.1 低效算法
每次将数组元素整体循环右移一个位置,循环 M 次。
6.2 高效算法
观察 初始数组元素排列 和 最终结果数组元素排列 之间的关系。
6.3 代码实现
C语言代码如下:
/* 标准输入输出和标准库头文件 */
#include<stdio.h>
#include<stdlib.h>
/* ----------------------移动数组元素高效算法所需要的函数----------------------------------- */
/* 函数功能:交换数组testArray中下标firstIndex和下标secondIndex的元素*/
/* 这里假设传入的参数都是正确的,即testArray不能为空,firstIndex和secondIndex下标值是合法的(不能越界)。*/
/* 移动数组元素高效算法所需要的函数 */
void swap(int testArray[], int firstIndex, int secondIndex)
{
// 如果firstIndex和second指向的是同一个下标,则不做任何动作;
if (firstIndex == secondIndex)
{
// empty block.
}
else
{
// 交换两个元素
int temp = testArray[firstIndex];
testArray[firstIndex] = testArray[secondIndex];
testArray[secondIndex] = temp;
}
}
/* 函数功能:将数组testArray下标从leftIndex到rightIndex,包含leftIndex和rightIndex的元素进行反转(翻转)。*/
/* 这里假设传入的参数都是正确的,即testArray不能为空,leftIndex和rightIndex下标值是合法且合理的(不能越界并且leftIndex < rightIndex)。*/
/* 移动数组元素高效算法所需要的函数 */
void ReverseArray(int testArray[], int leftIndex, int rightIndex)
{
/* 双“指针”--i和j */
/* i从leftIndex开始,往下标值增大的方向移动 */
/* j从rightIndex开始,往下标值减小的方向移动 */
int i = leftIndex;
int j = rightIndex;
// 当满足 i<j 时,循环执行“交换testArray[i]和testArray[j]”的操作
while (i < j)
{
swap(testArray, i, j);
++i;
--j;
}
}
/* -----------------移动数组元素高效算法所需要的函数------------------------------- */
/* 自定义函数 */
/* 函数功能:将具有n个元素的数组元素循环右移m位后,并输出数组结果 */
/* 参数:n是数组大小,m是循环右移的位数 */
/* 这里假设传入参数都是合法的 */
void cyclicShiftRight(int n, int m)
{
// input.
/* 接收从控制台输入的数组元素 */
int *testArray = (int*)malloc(sizeof(int) * n);
for (int i = 0; i < n; ++i)
{
scanf("%d", testArray + i);
}
// process.
/* 实际移位的位数 */
m = m % n;
/* 如果移位位数为0,则不进行操作 */
if (0 == m)
{
// empty
}
/* 否则,需要将数组元素循环向右移动m位 */
else
{
/* 暴力(笨)方法:循环移动m次,每次将数组元素右移一位 */
/*
for (int i = 0; i < m; i++)
{
int temp = testArray[n-1];
for (int j = n - 2; j >= 0; --j)
{
testArray[j + 1] = testArray[j];
}
testArray[0] = temp;
}
*/
/* 高效算法 */
/* 将原数组看作两部分 */
/* 一部分下标从0到n-m-1,进行数组元素的反转 */
ReverseArray(testArray, 0, n - m - 1);
/* 另一部分下标从n-m到n-1,进行数组元素的反转 */
ReverseArray(testArray, n - m, n - 1);
/* 整个数组所有元素进行一次数组元素的反转 */
ReverseArray(testArray, 0, n - 1);
}
// output.
/* 输出数组元素 */
for (int i = 0; i < n; ++i)
{
printf("%d", *(testArray + i));
/* 确保最后一个元素后面没有多余的空格 */
if (i != n - 1)
{
printf(" ");
}
}
/* 输出完成后,换行 */
printf("\n");
/* 释放空间,避免内存泄漏 */
free(testArray);
testArray = NULL;
}
/* 主函数 */
int main()
{
/* N为数组大小(数组元素的个数) */
int N = 0;
/* M为循环右移的位数 */
int M = 0;
/* 从控制台接收输入 */
scanf("%d %d", &N, &M);
/* 循环右移操作 */
cyclicShiftRight(N, M);
return 0;
}
6.4 补充说明
如果在 Mircosoft Visual Studio 2015 或 Microsoft Visual Studio 2017上报错 “scanf not safe,please use scanf_s instead" ,
此时将 “scanf” 出现的地方替换成 “scanf_s” 就行。
7. 题目来源
PTA(Programming Teaching Assistant) https://pintia.cn/problem-sets/17/problems/262
题目所有权归 PTA 所有。