問題描述:
數組裏有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