對於有k個元素的數組int a[k]={.......};寫一個高效算法將數組內容循環左移m位,
比如:int a[6]={1,2,3,4,5,6},循環左移3位後得到結果{4,5,6,1,2,3}.
要求:
1.不允許另外申請數組空間,但可以申請少許變量;
2.不允許採用每次左移。
最直觀的想法,就是從第一個元素開始,把他一步移動到最終目的位置,而該位置原有的元素的值取出,移動到它的新位置。遞歸進行這個步驟。
首先,我們在數學上很容易理解,這是一個一一對應映射,絕不會在一個位置上出現兩次移動。所以不會出現移動的遞歸過程中途指向了已經移動過的元素。
那麼,這個遞歸過程唯一的終止條件就是,當前移動的元素,目的位置就是移動過程的起始位置。
如果按照元素的索引下標標示元素,0到k-1中的任意元素i,會移動到什麼位置?
對於任意的i,它的新位置i'=((k-m) + i)%k. (注意這裏不是 (i-m)%k ,主要是因爲i-m小於零的時候,負數對k取模爲負數。所以i-m加上k使模落在0~k-1內)
那麼,我們可以定義這個循環鏈,取整數i0,使得0=<i0<m,定義i1=((k-m) + i0)%k,i2=((k-m) + i1)%k....ix=((k-m) + ix-1)%k.當ix=i0時循環終止。此時有i0 = ix=((k-m) + ix-1)%k=.... =(x(k-m) + i0 )%k
因爲我們知道0<i0 <m,所以有0=x(k-m)%k,也就是說,x(k-m)=yk,因爲我們是遇到第一個x(k-m)=yk就終止,顯然等號左右的值x(k-m)或yk就是k-m和k的最小公倍數。根據數論知識,我們知道,k,m,k-m這三個數有最大公約數q,使得k=aq,m=bq,k-m=cq,a,b,c三個數兩兩互質,x(k-m)=yk => xcq = yaq => xc = ya(又因爲a,c互質)進而推出x=a,y=c。也就是說,這個遞歸過程會經歷a步(x=a)。
因爲我們知道整個過程一共a步,k=aq,那麼i到ix的序列會形成一個步長爲q的等差序列,所以我們要移動整個0到k-1的區間,應該對0到q-1的元素應用這個遞歸算法。也就是說總共需要對q=gcd(k,k-m)個循環鏈進行遞歸過程。
該算法的時間複雜度爲O(n),空間複雜度爲O(1).
1: int CommonFactor(int dividend,int divisor)//求最大公約數函數
2: {
3: if(dividend<divisor)
4: {
5: int temp=dividend;
6: dividend=divisor;
7: divisor=temp;
8: }
9: int remainder=dividend%divisor;
10: while(remainder!=0)
11: {
12: dividend=divisor;
13: divisor=remainder;
14: remainder=dividend%divisor;
15: }
16: return divisor;
17: }
18:
19: void shift1(int a[],int k,int m)//k是數組a的元素個數,m是循環左移m位
20: {
21: int i,j,n,q=CommonFactor(k,k-m);
22: for(n=0;n<q;n++)
23: {
24: int temp=x[n]; i=n;j=(i+m)%k;
25: while(j!=n)
26: {
27: x[i]=x[j];
28: i=j;
29: j=(i+m)%k;
30: }
31: x[i]=temp;
32: }
33: }
34:
35: 同理可以分析循環右移m位的算法
36: void shift1(int a[],int k,int m)//k是數組a的元素個數,m是循環右移m位
37: {
38: int i,j,n,q=CommonFactor(k,m);
39: for(n=0;n<q;n++)
40: {
41: int temp=x[n]; i=n;j=(k+i-m)%k;
42: while(j!=n)
43: {
44: x[i]=x[j];
45: i=j;
46: j=(k+i-m)%k;
47: }
48: x[i]=temp;
49: }
50: }
51:
本文來自:http://www.crazycoder.cn/Arithmetic/Article31227.html