实验案例2-2:数组元素循环右移问题

          1008 数组元素循环右移问题 (20分)

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

输入格式:

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

输出格式:

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

输入样例:

6 2
1 2 3 4 5 6

输出样例:

5 6 1 2 3 4

1.C++方法,利用rotate函数

rotate(beg, mid, end),该函数接受三个迭代器, 将迭代器所在的容器的元素循环移动,使得mid元素成为首元素,随后是mid + 1到end之前的元素,
再接着是beg到mid 之间的元素,返回一个迭代器,指向原来在beg位置的元素
rotate_copy(beg, mid, end, dest),即将变换过后的元素保存在目的容器的dest位置

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <vector>
 4 
 5 using namespace std;
 6 
 7 int main()
 8 {
 9     int N, M, i;
10     vector<int> vec;
11     cin >> N >> M;
12     int mount = N;
13     while (mount--)
14     {
15         cin >> i;
16         vec.push_back(i);
17 
18     }
19     rotate(vec.begin(),  vec.end() - (M % N), vec.end());        //循环右移
20     //rotate(vec.begin(), vec.begin() + (M % N), vec.end());    //循环左移
21     vector<int>::const_iterator iter;
22     for (iter = vec.begin(); iter != vec.end() - 1; ++iter)
23         cout << *iter << " ";
24     cout << *iter << endl;
25     return 0;
26 }

2.C的一般方法:

先写一个循环右移一位的函数,然后调用该函数M次, 即可使数组循环右移M位

 1 #include <stdio.h>
 2 #define MAXN 100
 3 
 4 void CircleMove(int *array, int N);
 5 
 6 int main()
 7 {
 8     int Number[MAXN], N, M;
 9     int i;
10     scanf_s("%d %d", &N, &M);
11     for (i = 0; i < N; i++)
12     {
13         scanf_s("%d", &Number[i]);
14     }
15     M %= N;            //当M大于等于N时转化成等价的小于N的数
16     for (i = 0; i < M; i++)            //调用移位函数M次
17         CircleMove(Number, N);
18     for (i = 0; i < N -1; i++)
19         printf("%d ", Number[i]);
20     printf("%d", Number[i]);
21     return 0;
22 }
23 
24 void CircleMove(int *array, int N)
25 {
26     int i, ArrayEnd;
27     ArrayEnd = array[N - 1];        //先最后一个元素记录下来,因为待会会被覆盖掉
28     for (i = N -1; i > 0; i--)
29         array[i] = array[i - 1];
30     array[0] = ArrayEnd;
31 }

 

3.C的简单解法

先用宏定义define 定义一个两个数相交换的的函数,该函数利用了三次异或运算符      

异或运算符有一个性质,假如 a ^ b = c;那么 a ^ c = b; b ^ c = a;

#define Swap(a, b) a^ = b, b ^= a, a ^= b;    //交换两个数的宏定义函数,

对这个函数的解读:

 a ^= b 就是  a = a ^ b,此时a 等于上面说的C

b ^= a,就是 b = b ^ a,注意,这里的a 已经变成C了,所以相当于 b = b ^ C ---> b = a;

同理,a ^= b,就是  a = a ^ b,需要注意的是,等式右边的a在第一步变换中变成了C,b 在第二步变换中变成了a, 所以这个等式就相当于a = C ^ a ------> a = b;

所以就完成了交换。

接下来程序中利用三次逆转,实现了循环右移M位,(这种方法很神奇)

第一次:逆转整个数组

第二次:在第一步的基础上逆转数组前M个元素

第三次: 逆转数组后N- M个元素

 (Swap()函数可以直接按使用algorithm 中已经有的swap()函数,效果是一样的。)

 1 #include <stdio.h>
 2 #define MAXN 100
 3 #define Swap(a, b) a^= b, b^= a, a ^= b;    //通过连续三次以后运算符交换a与b
 4 
 5 void RightShift(int array[], int N, int M);
 6 
 7 int main()
 8 {
 9     int Number[MAXN], N, M;
10     int i;
11     scanf_s("%d %d", &N, &M);
12     for (i = 0; i < N; i++)
13         scanf_s("%d", &Number[i]);
14     M %= N;
15     RightShift(Number, N);
16     for (i = 0; i < N - 1; i++)
17         printf("%d", Number[i]);
18     printf("%d", Number[N - 1]);
19     return 0;
20 }
21 
22 void RightShift(int array[], int N, int M)
23 {
24     int i, j;
25     if (M > 0 && M < N)
26     {
27         for (i = 0, j < N - 1; i < j; i++, j--)        //逆转N个数据
28             Swap(array[i], array[j]);                
29         for (i = 0, j < M - 1; i < j; i++, j--)        //逆转前M个数据
30             Swap(array[i], array[j]);
31         for (i = M, j = N - 1; i < j; i++, j--)        //逆转后N - M个数据
32             Swap(array[i], array[j]);
33     }
34 }

逆转函数可以使用头文件 algorithm 中的 reverse()函数,这个函数可以将给定数组区间或迭代器区间的内容反转,使用方法是:reverse(a, a + 4); 加上a是数组,所以上面方法三的代码可以进一步得到简化:使用这个头文件必须包含using namespace std这个命名空间

 1 #include <stdio.h>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 int arr[110] = { 0 };
 6 
 7 int main()
 8 {
 9     int n, m;
10     scanf("%d %d", &n, &m);
11     for (int i = 0; i < n; i++){
12         scanf("%d", &arr[i]);
13     }
14     m %= n;            // 修正m的值
15     reverse(arr, arr + n);            // 反转 0 - n之间的数字
16     reverse(arr, arr + m);            // 反转 0 - (m-1)之间的数字
17     reverse(arr + m, arr + n);        // 反转 m - (n-1)之间的数字
18 
19     for (int i = 0; i < n; i++){
20         if (i > 0)
21             printf(" ");
22         printf("%d", arr[i]);
23     }
24     return 0;
25 }

4 . 利用n和m的最大公约数(这个方法还不是很理解)

从N - M为开始移动,一直到(n - m + d), d 是 m 和 n 的最大公约数。每次将当前元素存储在一个临时变量上,然后将(i - M)位的元素移动到当前位置,直到(i- M) == (i + M) % N,此时将临时变量上的元素放置到该位置上,进入下次循环

 1 int ans[110];
 2 
 3 int gcd(int a, int b){
 4     return !b ? a : gcd(b, a % b);
 5 }
 6 
 7 int main()
 8 {
 9     // 读取输入
10     freopen("in.txt", "r", stdin);
11     int n, m;
12     int temp, pos, next;
13     scanf("%d %d", &n, &m);
14     for (int i = 0; i < n; i++){
15         scanf("%d", &ans[i]);
16     }
17 
18     m %= n;        // 修正m
19 
20     // 如果m == 0,则不用移动,m != 0才需移动
21     if (m != 0){
22         int d = gcd(m, n);
23         for (int i = n - m; i < n - m + d; i++){
24             // 每次将当前元素存储在一个临时变量上
25             temp = ans[i];
26 
27             // 然后将(i - M)位的元素移动到当前位置, 直到(i- M) == (i + M) % N
28             pos = i;
29             do{
30                 // 计算下一个要处理的位置
31                 next = (pos - m + n) % n;
32                 // 如果下一个位置不是初始点
33                 // 则把下一个位置的元素赋值给当前处理位置
34                 if (next != i)
35                     ans[pos] = ans[next];
36                 else
37                     ans[pos] = temp;
38                 pos = next;
39             } while (pos != i);
40         }
41     }
42 
43     // 输出数组
44     for (int i = 0; i < n; i++){
45         printf("%d", ans[i]);
46         if (i < n- 1)
47             printf(" ");
48     }
50 
51     fclose(stdin);
52     return 0;
53 }
原文地址:https://www.cnblogs.com/hi3254014978/p/9683278.html