數組的循環移動是考查的算法中比較常見的,有循環左移,循環右移之類的。當然,不會直接就考查移動的算法,還會有一定的時間複雜度,空間複雜度的要求之類的,這纔是算法要求的,所以這兩天剛好看到類似的題目,就記錄一下。
題目要求
如有數組abcd1234,讓數組向右邊移動兩位,變成了34abcd12。這就是數組的循環右移操作,那麼要求在儘可能少的時間複雜度下實現將整個數組移動m次的這個操作。
不考慮時間複雜度的實現
對於這個移動,我們可以考慮將整個數組一次一次的移動,移動m次。實現代碼如下:
for(int i = 0;i < m;i++){
int temp = a[n-1];
int j;
for(j = n-2;j >= 0;j--){
a[j+1] = a[j];
}//當j=0的時候結束循環,但是還是會減一
a[j+1] = temp;
}
上訴的代碼就是最容易實現的,內層循環是實現一次移動,外層循環加上之後實現m次移動,時間複雜度爲O(m*n).
考慮時間複雜度的實現
我們還是把字符串看成有兩段組成的,記位XY。左旋轉相當於要把字符串XY變成YX。我們先在字符串上定義一種翻轉的操作,就是翻轉字符串中字符的先後順序。把X翻轉後記爲XT。顯然有(XT)T=X。
我們首先對X和Y兩段分別進行翻轉操作,這樣就能得到XTYT。接着再對XTYT進行翻轉操作,得到(XTYT)T=(YT)T(XT)T=YX。正好是我們期待的結果。
分析到這裏我們再回到原來的題目。我們要做的僅僅是把字符串分成兩段,第一段爲前面N-M個字符,其餘的字符分到第二段(剩餘M個字符)。再定義一個翻轉字符串的函數,按照前面的步驟翻轉三次就行了,就是先翻轉前面一段字符,再翻轉後面一段字符,最後翻轉整個字符串。這樣的時間複雜度和空間複雜度都合乎要求。
實現代碼如下:
//翻轉函數
void reverse(int *a,int start,int end);
{
int temp;
for(;start<end;start++,end--){
temp = a[end];
a[end] = a[start];
a[start] = temp;
}
}
//主函數
void shift(int *a,int n,int m)
{
m = m % n;
reverse(a,0,n-m-1);//前面部分逆序
reverse(a,n-m,n-1);//後面m個字符逆序
reverse(a,0,n-1);//整體逆序
}
這樣只會就能實現時間複雜度爲O(n)的算法了,這裏其實用到了漢諾塔的類似的思想。