问题描述:
数组里有n个数据,要将他们顺序循环向后移k位,即前面元素向后移动k位,后面的元素向前移动k位。因为n可能很大,不容许用2*n以上的空间完成此题。算法
法1:算法设计:可开辟另一个与所需处理数组空间大小相同的的数组来存处理后的元素。循环过程当中,0~n-k-1个元素的确是向后移动,然后面的则是顺序从后向前移动。所以用取模运算就能够决定每一个元素移动后的的最终位置。
程序代码:数组
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #define N 10 int main(){ int n,k,i; int a[N],b[N]; printf("输入数组中数的个数:"); scanf("%d",&n); printf("\n输入数的移动步数:"); scanf("%d", &k);; printf("\n输入每一个数字:"); for (i = 0; i < n; i++) scanf("%d", &a[i]); for(i=0;i<n;i++) b[(i+k)%n]=a[i]; printf("\n移动后输出:"); for (i = 0; i < n; i++) { printf("%d ", a[i]); } system("pause"); return 0; }
法2:算法设计:将最后一个元素用临时变量存储,其他元素逐个向后移动一个,而后用临时变量最后一个元素放入第一个空间中,把这个过程重复k次,则达到要求。
程序代码:ide
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #define N 10 int main(){ int n,k,i,j; int a[N]; printf("输入数组中数的个数:"); scanf("%d",&n); printf("\n输入数的移动步数:"); scanf("%d", &k);; printf("\n输入每一个数字:"); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } for(i=0;i<k;i++) {b=a[n-1] for(j=n-1;j>=0;j--) a[j]=a[j-1]; a[0]=b; } printf("\n移动后输出:"); for (i = 0; i < n; i++) { printf("%d ", a[i]); } system("pause"); return 0; }
法3:(算法书上)利用一个临时的存储空间,把每个数据一次移动到位。
把位置差为所需移动步长的分为一个组,进行移动。所以要求出组数和一个组里有多少个元素,而后进行组内移动。
例如:
当元素个数为6,移动步长为3时,0,1,2,3,4,5移动的结果是3,4,5,0,1,2。则移动组数可分为3组:(0->3,3->0)(1->4,4->1)
(2->5,5->2)。当移动步长为2时,可分为2组:(0->2,2->4,4->0) (1->3,3->5,5->1)
由实例分析,循环组数为元素个数和步长的最大公约数,每组的个数也可由元素个数除以组数算出。
为何组数是最大公约数呢?书上没有说明,所以我试着分析一下。
一组数据中,最后一个元素确定是又回到第一个元素上的,所以组数确定是要能被元素的个数整除的,因为每一个元素之间是隔了k个(步长)元素,所以一组元素加上每一个元素下标取余元素总数都应为相同的数才为一组,所以组数为元素个数和步长的最大公约数。设计
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #define N 10 int couculateM(int x,int y) { int r; while (y) { r = x%y; x = y; y = r; } return x; } int main(){ int n,m,k,i,b,b1,t,j; int a[N]; printf("输入数组中数的个数:"); scanf("%d",&n); printf("\n输入数的移动步数:"); scanf("%d", &k);; printf("\n输入每一个数字:"); for (i = 0; i < n; i++) { scanf("%d", &a[i]); } m = couculateM(n, k); for (i = 0; i < m; i++) { b = a[i]; t = i; for (j = 0 ; j <n/m; j++) { t=(t+k)%n; b1=a[t]; a[t] = b; b = b1; } /* 从前日后移会用b、b1两个临时变量,从最后一个开始,把前一个以此向 后移,与法2的思想同样,只用一个临时变量就可解决问题。而且减小 赋值次数,具体实现。 for (i = 0; i < m; i++) { b = a[(i+(n/m-1)*k)%n]; t = i + (n / m - 1)*k; for (j = n/m; j >0; j--) { a[t%n] = a[(t - k) % n]; t = t - k; } a[i] = b; }*/ } printf("\n移动后输出:"); for (i = 0; i < n; i++) { printf("%d ", a[i]); } system("pause"); return 0; }
总结:
法1算法思想很简单,也很好理解,但会占用一个元素空间。
法2重复作一件简单事情,简化了问题,可是重复次数太多,效率并不高。
法3为分而治之的思想,将元素分为m各组,每组重复相同的事情,效率比前面两种方法都高code